Fossil SCM

Merge from trunk.

brickviking 2025-10-23 08:14 bv-infotool merge
Commit 0ffacb525a925ba8da3152c9041fdf98e67b14f10faf30dc685dea5a8ab4e9c6
147 files changed +1 -1 +826 -980 +2521 -708 +178 -42 +2 -2 +80 -25 +24 -31 +12 -8 +82 -36 +18 +16 -6 +225 +1 -1 +7 -27 +1 -1 +2 -1 +7 -1 +19 -22 +26 -4 +46 -8 +18 -3 +19 -13 +1 -1 +1 +29 -15 +3 -3 +2 -1 +18 -8 +2 -2 +9 -11 +6 -5 +4 -8 +2 -7 +3 -4 -1 +32 +2 -2 +4 -4 +16 -12 +27 -10 +73 -41 +1 -1 +131 -142 +7 -2 +13 +1 -1 +1 -1 +2 -2 +2 -2 +1 -1 +243 -92 +410 +6 -6 +4 -2 +156 -85 +3 +2 -2 +15 -5 +21 -62 +1 -1 +531 -34 +1 -1 +3 -3 +284 -164 +4 +12 -8 +9 +3 +4 -3 +42 -1 +24 -18 -139 +1 -1 +1 -1 -138 +138 +2 -2 +1 +10 -4 +13 +12 +3 -3 +11 -11 +11 -11 +147 -31 +5 -5 +2 -2 +7 -7 +6 -6 +5 -5 +2 -2 +6 -6 +1 -1 +3 -3 +2 -2 +409 -345 +8 -8 +1 -1 +1 -1 +1 -1 +2 -2 +4 -4 +1 -2 +1 -1 +4 -4 +6 -6 +5 -5 +2 -2 +1 -1 +1 -1 +1 -1 +1 -1 +9 -9 +22 -22 +24 -24 +18 -18 +5 +2 -2 +1 -1 +7 -7 +4 -4 +1 -1 +2 -2 +4 -4 +1 -1 +1 -1 +4 -4 +1 -1 +1 -1 +3 -3 +1 -1 +1 -1 +1 -1 +5 -5 +1 -1 +1 -1 +1 -1 +7 -7 +2 -2 +2 -2 +8 -8 +7 -7 +1 -1 +1 -1 +6 -6
~ VERSION ~ extsrc/shell.c ~ extsrc/sqlite3.c ~ extsrc/sqlite3.h ~ src/add.c ~ src/branch.c ~ src/browse.c ~ src/captcha.c ~ src/cgi.c ~ src/chat.c ~ src/checkin.c ~ src/checkout.c ~ src/clearsign.c ~ src/clone.c ~ src/comformat.c ~ src/content.c ~ src/cookies.c ~ src/copybtn.js ~ src/db.c ~ src/default.css ~ src/deltafunc.c ~ src/diff.c ~ src/diff.tcl ~ src/diffcmd.c ~ src/dispatch.c ~ src/doc.c ~ src/extcgi.c ~ src/finfo.c ~ src/forum.c ~ src/fossil.copybutton.js ~ src/fossil.dom.js ~ src/fossil.numbered-lines.js ~ src/fossil.page.chat.js ~ src/fossil.page.pikchrshow.js ~ src/fossil.page.pikchrshowasm.js ~ src/fossil.page.ticket.js ~ src/fossil.page.wikiedit.js ~ src/graph.js ~ src/href.js ~ src/http.c ~ src/info.c ~ src/json.c ~ src/login.c ~ src/main.c ~ src/main.mk ~ src/manifest.c ~ src/markdown.md ~ src/match.c ~ src/pikchrshow.c ~ src/printf.c ~ src/regexp.c ~ src/robot.c ~ src/search.c ~ src/security_audit.c ~ src/setup.c ~ src/sitemap.c ~ src/skins.c ~ src/stash.c ~ src/style.c ~ src/tag.c ~ src/tar.c ~ src/th_lang.c ~ src/th_main.c ~ src/timeline.c ~ src/tkt.c ~ src/tktsetup.c ~ src/unversioned.c ~ src/url.c ~ src/user.c ~ src/util.c ~ src/xfer.c ~ src/zip.c ~ test/commit-warning.test ~ test/json.test ~ test/set-manifest.test - test/settings.test ~ test/settings.test.off + test/settings.test.off ~ tools/makeheaders.c ~ tools/makemake.tcl ~ win/Makefile.dmc ~ win/Makefile.mingw ~ win/Makefile.msc ~ www/aboutcgi.wiki ~ www/aboutdownload.wiki ~ www/alerts.md ~ www/antibot.wiki ~ www/backoffice.md ~ www/backup.md ~ www/blame.wiki ~ www/blockchain.md ~ www/branching.wiki ~ www/build.wiki ~ www/caps/admin-v-setup.md ~ www/caps/login-groups.md ~ www/caps/ref.html ~ www/cgi.wiki ~ www/changes.wiki ~ www/chat.md ~ www/childprojects.wiki ~ www/chroot.md ~ www/ckout-workflows.md ~ www/co-vs-up.md ~ www/concepts.wiki ~ www/containers.md ~ www/contribute.wiki ~ www/customskin.md ~ www/defcsp.md ~ www/delta_format.wiki ~ www/embeddeddoc.wiki ~ www/env-opts.md ~ www/event.wiki ~ www/fileedit-page.md ~ www/forum.wiki ~ www/fossil-v-git.wiki ~ www/gitusers.md ~ www/globs.md ~ www/glossary.md ~ www/grep.md ~ www/hashes.md ~ www/hashpolicy.wiki ~ www/hints.wiki ~ www/index.wiki ~ www/inout.wiki ~ www/interwiki.md ~ www/javascript.md ~ www/json-api/intro.md ~ www/loadmgmt.md ~ www/makefile.wiki ~ www/mdtest/test1.md ~ www/mirrorlimitations.md ~ www/mirrortogithub.md ~ www/password.wiki ~ www/patchcmd.md ~ www/private.wiki ~ www/quickstart.wiki ~ www/rebaseharm.md ~ www/relatedwork.md ~ www/selfhost.wiki ~ www/server/index.html ~ www/server/windows/service.md ~ www/serverext.wiki ~ www/ssl-server.md ~ www/sync.wiki ~ www/tech_overview.wiki ~ www/th1.md ~ www/unvers.wiki
+1 -1
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1
-2.27
1
+2.28
22
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 2.27
2
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 2.28
2
+826 -980
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -122,10 +122,13 @@
122122
typedef sqlite3_int64 i64;
123123
typedef sqlite3_uint64 u64;
124124
typedef unsigned char u8;
125125
#include <ctype.h>
126126
#include <stdarg.h>
127
+#ifndef _WIN32
128
+# include <sys/time.h>
129
+#endif
127130
128131
#if !defined(_WIN32) && !defined(WIN32)
129132
# include <signal.h>
130133
# if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
131134
# include <pwd.h>
@@ -646,12 +649,23 @@
646649
if( a==0 ) a = "";
647650
if( b==0 ) b = "";
648651
return strncmp(a,b,n);
649652
}
650653
651
-/* Return the current wall-clock time */
654
+/* Return the current wall-clock time in microseconds since the
655
+** Unix epoch (1970-01-01T00:00:00Z)
656
+*/
652657
static sqlite3_int64 timeOfDay(void){
658
+#if defined(_WIN64)
659
+ sqlite3_uint64 t;
660
+ FILETIME tm;
661
+ GetSystemTimePreciseAsFileTime(&tm);
662
+ t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
663
+ t += 116444736000000000LL;
664
+ t /= 10;
665
+ return t;
666
+#elif defined(_WIN32)
653667
static sqlite3_vfs *clockVfs = 0;
654668
sqlite3_int64 t;
655669
if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
656670
if( clockVfs==0 ) return 0; /* Never actually happens */
657671
if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
@@ -659,11 +673,16 @@
659673
}else{
660674
double r;
661675
clockVfs->xCurrentTime(clockVfs, &r);
662676
t = (sqlite3_int64)(r*86400000.0);
663677
}
664
- return t;
678
+ return t*1000;
679
+#else
680
+ struct timeval sNow;
681
+ (void)gettimeofday(&sNow,0);
682
+ return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
683
+#endif
665684
}
666685
667686
#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
668687
#include <sys/time.h>
669688
#include <sys/resource.h>
@@ -704,12 +723,12 @@
704723
static void endTimer(FILE *out){
705724
if( enableTimer ){
706725
sqlite3_int64 iEnd = timeOfDay();
707726
struct rusage sEnd;
708727
getrusage(RUSAGE_SELF, &sEnd);
709
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
710
- (iEnd - iBegin)*0.001,
728
+ sqlite3_fprintf(out, "Run Time: real %.6f user %.6f sys %.6f\n",
729
+ (iEnd - iBegin)*0.000001,
711730
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
712731
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
713732
}
714733
}
715734
@@ -783,14 +802,23 @@
783802
static void endTimer(FILE *out){
784803
if( enableTimer && getProcessTimesAddr){
785804
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
786805
sqlite3_int64 ftWallEnd = timeOfDay();
787806
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
788
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
789
- (ftWallEnd - ftWallBegin)*0.001,
807
+#ifdef _WIN64
808
+ /* microsecond precision on 64-bit windows */
809
+ sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
810
+ (ftWallEnd - ftWallBegin)*0.000001,
811
+ timeDiff(&ftUserBegin, &ftUserEnd),
812
+ timeDiff(&ftKernelBegin, &ftKernelEnd));
813
+#else
814
+ /* millisecond precisino on 32-bit windows */
815
+ sqlite3_fprintf(out, "Run Time: real %.3f user %.3f sys %.3f\n",
816
+ (ftWallEnd - ftWallBegin)*0.000001,
790817
timeDiff(&ftUserBegin, &ftUserEnd),
791818
timeDiff(&ftKernelBegin, &ftKernelEnd));
819
+#endif
792820
}
793821
}
794822
795823
#define BEGIN_TIMER beginTimer()
796824
#define END_TIMER(X) endTimer(X)
@@ -1126,11 +1154,11 @@
11261154
}
11271155
if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80
11281156
&& (z[3] & 0xc0)==0x80
11291157
){
11301158
*pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
1131
- | (z[4] & 0x3f);
1159
+ | (z[3] & 0x3f);
11321160
return 4;
11331161
}
11341162
*pU = 0;
11351163
return 1;
11361164
}
@@ -1189,18 +1217,28 @@
11891217
** since with %*.*s the width is measured in bytes, not characters.
11901218
**
11911219
** Take into account zero-width and double-width Unicode characters.
11921220
** In other words, a zero-width character does not count toward the
11931221
** the w limit. A double-width character counts as two.
1222
+**
1223
+** w should normally be a small number. A couple hundred at most. This
1224
+** routine caps w at 100 million to avoid integer overflow issues.
11941225
*/
11951226
static void utf8_width_print(FILE *out, int w, const char *zUtf){
11961227
const unsigned char *a = (const unsigned char*)zUtf;
1228
+ static const int mxW = 10000000;
11971229
unsigned char c;
11981230
int i = 0;
11991231
int n = 0;
12001232
int k;
1201
- int aw = w<0 ? -w : w;
1233
+ int aw;
1234
+ if( w<-mxW ){
1235
+ w = -mxW;
1236
+ }else if( w>mxW ){
1237
+ w= mxW;
1238
+ }
1239
+ aw = w<0 ? -w : w;
12021240
if( zUtf==0 ) zUtf = "";
12031241
while( (c = a[i])!=0 ){
12041242
if( (c&0xc0)==0xc0 ){
12051243
int u;
12061244
int len = decodeUtf8(a+i, &u);
@@ -1323,11 +1361,11 @@
13231361
13241362
/*
13251363
** This routine reads a line of text from FILE in, stores
13261364
** the text in memory obtained from malloc() and returns a pointer
13271365
** to the text. NULL is returned at end of file, or if malloc()
1328
-** fails.
1366
+** fails, or if the length of the line is longer than about a gigabyte.
13291367
**
13301368
** If zLine is not NULL then it is a malloced buffer returned from
13311369
** a previous call to this routine that may be reused.
13321370
*/
13331371
static char *local_getline(char *zLine, FILE *in){
@@ -1334,10 +1372,14 @@
13341372
int nLine = zLine==0 ? 0 : 100;
13351373
int n = 0;
13361374
13371375
while( 1 ){
13381376
if( n+100>nLine ){
1377
+ if( nLine>=1073741773 ){
1378
+ free(zLine);
1379
+ return 0;
1380
+ }
13391381
nLine = nLine*2 + 100;
13401382
zLine = realloc(zLine, nLine);
13411383
shell_check_oom(zLine);
13421384
}
13431385
if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
@@ -1417,14 +1459,18 @@
14171459
return -1;
14181460
}
14191461
14201462
/*
14211463
** Interpret zArg as an integer value, possibly with suffixes.
1464
+**
1465
+** If the value specified by zArg is outside the range of values that
1466
+** can be represented using a 64-bit twos-complement integer, then return
1467
+** the nearest representable value.
14221468
*/
14231469
static sqlite3_int64 integerValue(const char *zArg){
1424
- sqlite3_int64 v = 0;
1425
- static const struct { char *zSuffix; int iMult; } aMult[] = {
1470
+ sqlite3_uint64 v = 0;
1471
+ static const struct { char *zSuffix; unsigned int iMult; } aMult[] = {
14261472
{ "KiB", 1024 },
14271473
{ "MiB", 1024*1024 },
14281474
{ "GiB", 1024*1024*1024 },
14291475
{ "KB", 1000 },
14301476
{ "MB", 1000000 },
@@ -1443,46 +1489,54 @@
14431489
}
14441490
if( zArg[0]=='0' && zArg[1]=='x' ){
14451491
int x;
14461492
zArg += 2;
14471493
while( (x = hexDigitValue(zArg[0]))>=0 ){
1494
+ if( v > 0x0fffffffffffffffULL ) goto integer_overflow;
14481495
v = (v<<4) + x;
14491496
zArg++;
14501497
}
14511498
}else{
14521499
while( IsDigit(zArg[0]) ){
1453
- v = v*10 + zArg[0] - '0';
1500
+ if( v>=922337203685477580LL ){
1501
+ if( v>922337203685477580LL || zArg[0]>='8' ) goto integer_overflow;
1502
+ }
1503
+ v = v*10 + (zArg[0] - '0');
14541504
zArg++;
14551505
}
14561506
}
14571507
for(i=0; i<ArraySize(aMult); i++){
14581508
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
1509
+ if( 0x7fffffffffffffffULL/aMult[i].iMult < v ) goto integer_overflow;
14591510
v *= aMult[i].iMult;
14601511
break;
14611512
}
14621513
}
1463
- return isNeg? -v : v;
1514
+ if( isNeg && v>0x7fffffffffffffffULL ) goto integer_overflow;
1515
+ return isNeg? -(sqlite3_int64)v : (sqlite3_int64)v;
1516
+integer_overflow:
1517
+ return isNeg ? (i64)0x8000000000000000LL : 0x7fffffffffffffffLL;
14641518
}
14651519
14661520
/*
14671521
** A variable length string to which one can append text.
14681522
*/
14691523
typedef struct ShellText ShellText;
14701524
struct ShellText {
1471
- char *z;
1472
- int n;
1473
- int nAlloc;
1525
+ char *zTxt; /* The text */
1526
+ i64 n; /* Number of bytes of zTxt[] actually used */
1527
+ i64 nAlloc; /* Number of bytes allocated for zTxt[] */
14741528
};
14751529
14761530
/*
14771531
** Initialize and destroy a ShellText object
14781532
*/
14791533
static void initText(ShellText *p){
14801534
memset(p, 0, sizeof(*p));
14811535
}
14821536
static void freeText(ShellText *p){
1483
- free(p->z);
1537
+ sqlite3_free(p->zTxt);
14841538
initText(p);
14851539
}
14861540
14871541
/* zIn is either a pointer to a NULL-terminated string in memory obtained
14881542
** from malloc(), or a NULL pointer. The string pointed to by zAppend is
@@ -1503,30 +1557,30 @@
15031557
for(i=0; i<nAppend; i++){
15041558
if( zAppend[i]==quote ) len++;
15051559
}
15061560
}
15071561
1508
- if( p->z==0 || p->n+len>=p->nAlloc ){
1562
+ if( p->zTxt==0 || p->n+len>=p->nAlloc ){
15091563
p->nAlloc = p->nAlloc*2 + len + 20;
1510
- p->z = realloc(p->z, p->nAlloc);
1511
- shell_check_oom(p->z);
1564
+ p->zTxt = sqlite3_realloc64(p->zTxt, p->nAlloc);
1565
+ shell_check_oom(p->zTxt);
15121566
}
15131567
15141568
if( quote ){
1515
- char *zCsr = p->z+p->n;
1569
+ char *zCsr = p->zTxt+p->n;
15161570
*zCsr++ = quote;
15171571
for(i=0; i<nAppend; i++){
15181572
*zCsr++ = zAppend[i];
15191573
if( zAppend[i]==quote ) *zCsr++ = quote;
15201574
}
15211575
*zCsr++ = quote;
1522
- p->n = (int)(zCsr - p->z);
1576
+ p->n = (i64)(zCsr - p->zTxt);
15231577
*zCsr = '\0';
15241578
}else{
1525
- memcpy(p->z+p->n, zAppend, nAppend);
1579
+ memcpy(p->zTxt+p->n, zAppend, nAppend);
15261580
p->n += nAppend;
1527
- p->z[p->n] = '\0';
1581
+ p->zTxt[p->n] = '\0';
15281582
}
15291583
}
15301584
15311585
/*
15321586
** Attempt to determine if identifier zName needs to be quoted, either
@@ -1547,10 +1601,13 @@
15471601
}
15481602
15491603
/*
15501604
** Construct a fake object name and column list to describe the structure
15511605
** of the view, virtual table, or table valued function zSchema.zName.
1606
+**
1607
+** The returned string comes from sqlite3_mprintf() and should be freed
1608
+** by the caller using sqlite3_free().
15521609
*/
15531610
static char *shellFakeSchema(
15541611
sqlite3 *db, /* The database connection containing the vtab */
15551612
const char *zSchema, /* Schema of the database holding the vtab */
15561613
const char *zName /* The name of the virtual table */
@@ -1587,13 +1644,13 @@
15871644
}
15881645
appendText(&s, ")", 0);
15891646
sqlite3_finalize(pStmt);
15901647
if( nRow==0 ){
15911648
freeText(&s);
1592
- s.z = 0;
1649
+ s.zTxt = 0;
15931650
}
1594
- return s.z;
1651
+ return s.zTxt;
15951652
}
15961653
15971654
/*
15981655
** SQL function: strtod(X)
15991656
**
@@ -1692,11 +1749,11 @@
16921749
if( z==0 ){
16931750
z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
16941751
}else{
16951752
z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
16961753
}
1697
- free(zFake);
1754
+ sqlite3_free(zFake);
16981755
}
16991756
if( z ){
17001757
sqlite3_result_text(pCtx, z, -1, sqlite3_free);
17011758
return;
17021759
}
@@ -3667,11 +3724,12 @@
36673724
iExp -= p->nFrac;
36683725
p->nFrac = 0;
36693726
}
36703727
}
36713728
if( iExp>0 ){
3672
- p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
3729
+ p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
3730
+ + (sqlite3_int64)iExp + 1 );
36733731
if( p->a==0 ) goto new_from_text_failed;
36743732
memset(p->a+p->nDigit, 0, iExp);
36753733
p->nDigit += iExp;
36763734
}
36773735
}else if( iExp<0 ){
@@ -3686,18 +3744,23 @@
36863744
iExp -= nExtra;
36873745
p->nFrac = p->nDigit - 1;
36883746
}
36893747
}
36903748
if( iExp>0 ){
3691
- p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
3749
+ p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
3750
+ + (sqlite3_int64)iExp + 1 );
36923751
if( p->a==0 ) goto new_from_text_failed;
36933752
memmove(p->a+iExp, p->a, p->nDigit);
36943753
memset(p->a, 0, iExp);
36953754
p->nDigit += iExp;
36963755
p->nFrac += iExp;
36973756
}
36983757
}
3758
+ if( p->sign ){
3759
+ for(i=0; i<p->nDigit && p->a[i]==0; i++){}
3760
+ if( i>=p->nDigit ) p->sign = 0;
3761
+ }
36993762
return p;
37003763
37013764
new_from_text_failed:
37023765
if( p ){
37033766
if( p->a ) sqlite3_free(p->a);
@@ -3786,11 +3849,11 @@
37863849
}
37873850
if( p->isNull ){
37883851
sqlite3_result_null(pCtx);
37893852
return;
37903853
}
3791
- z = sqlite3_malloc( p->nDigit+4 );
3854
+ z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 );
37923855
if( z==0 ){
37933856
sqlite3_result_error_nomem(pCtx);
37943857
return;
37953858
}
37963859
i = 0;
@@ -3851,11 +3914,11 @@
38513914
}
38523915
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
38533916
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
38543917
nFrac = p->nFrac + (nDigit - p->nDigit);
38553918
nDigit -= nZero;
3856
- z = sqlite3_malloc( nDigit+20 );
3919
+ z = sqlite3_malloc64( (sqlite3_int64)nDigit+20 );
38573920
if( z==0 ){
38583921
sqlite3_result_error_nomem(pCtx);
38593922
return;
38603923
}
38613924
if( nDigit==0 ){
@@ -3896,17 +3959,25 @@
38963959
** pA!=0
38973960
** pA->isNull==0
38983961
** pB!=0
38993962
** pB->isNull==0
39003963
*/
3901
-static int decimal_cmp(const Decimal *pA, const Decimal *pB){
3964
+static int decimal_cmp(Decimal *pA, Decimal *pB){
39023965
int nASig, nBSig, rc, n;
3966
+ while( pA->nFrac>0 && pA->a[pA->nDigit-1]==0 ){
3967
+ pA->nDigit--;
3968
+ pA->nFrac--;
3969
+ }
3970
+ while( pB->nFrac>0 && pB->a[pB->nDigit-1]==0 ){
3971
+ pB->nDigit--;
3972
+ pB->nFrac--;
3973
+ }
39033974
if( pA->sign!=pB->sign ){
39043975
return pA->sign ? -1 : +1;
39053976
}
39063977
if( pA->sign ){
3907
- const Decimal *pTemp = pA;
3978
+ Decimal *pTemp = pA;
39083979
pA = pB;
39093980
pB = pTemp;
39103981
}
39113982
nASig = pA->nDigit - pA->nFrac;
39123983
nBSig = pB->nDigit - pB->nFrac;
@@ -4064,11 +4135,12 @@
40644135
if( pA==0 || pA->oom || pA->isNull
40654136
|| pB==0 || pB->oom || pB->isNull
40664137
){
40674138
goto mul_end;
40684139
}
4069
- acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
4140
+ acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
4141
+ (sqlite3_int64)pB->nDigit + 2 );
40704142
if( acc==0 ){
40714143
pA->oom = 1;
40724144
goto mul_end;
40734145
}
40744146
memset(acc, 0, pA->nDigit + pB->nDigit + 2);
@@ -4151,11 +4223,11 @@
41514223
r = -r;
41524224
}else{
41534225
isNeg = 0;
41544226
}
41554227
memcpy(&a,&r,sizeof(a));
4156
- if( a==0 ){
4228
+ if( a==0 || a==(sqlite3_int64)0x8000000000000000LL){
41574229
e = 0;
41584230
m = 0;
41594231
}else{
41604232
e = a>>52;
41614233
m = a & ((((sqlite3_int64)1)<<52)-1);
@@ -4426,516 +4498,10 @@
44264498
}
44274499
return rc;
44284500
}
44294501
44304502
/************************* End ../ext/misc/decimal.c ********************/
4431
-/************************* Begin ../ext/misc/percentile.c ******************/
4432
-/*
4433
-** 2013-05-28
4434
-**
4435
-** The author disclaims copyright to this source code. In place of
4436
-** a legal notice, here is a blessing:
4437
-**
4438
-** May you do good and not evil.
4439
-** May you find forgiveness for yourself and forgive others.
4440
-** May you share freely, never taking more than you give.
4441
-**
4442
-******************************************************************************
4443
-**
4444
-** This file contains code to implement the percentile(Y,P) SQL function
4445
-** and similar as described below:
4446
-**
4447
-** (1) The percentile(Y,P) function is an aggregate function taking
4448
-** exactly two arguments.
4449
-**
4450
-** (2) If the P argument to percentile(Y,P) is not the same for every
4451
-** row in the aggregate then an error is thrown. The word "same"
4452
-** in the previous sentence means that the value differ by less
4453
-** than 0.001.
4454
-**
4455
-** (3) If the P argument to percentile(Y,P) evaluates to anything other
4456
-** than a number in the range of 0.0 to 100.0 inclusive then an
4457
-** error is thrown.
4458
-**
4459
-** (4) If any Y argument to percentile(Y,P) evaluates to a value that
4460
-** is not NULL and is not numeric then an error is thrown.
4461
-**
4462
-** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
4463
-** infinity then an error is thrown. (SQLite always interprets NaN
4464
-** values as NULL.)
4465
-**
4466
-** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
4467
-** including CASE WHEN expressions.
4468
-**
4469
-** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
4470
-** one million (1,000,000) rows.
4471
-**
4472
-** (8) If there are no non-NULL values for Y, then percentile(Y,P)
4473
-** returns NULL.
4474
-**
4475
-** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
4476
-** returns the one Y value.
4477
-**
4478
-** (10) If there N non-NULL values of Y where N is two or more and
4479
-** the Y values are ordered from least to greatest and a graph is
4480
-** drawn from 0 to N-1 such that the height of the graph at J is
4481
-** the J-th Y value and such that straight lines are drawn between
4482
-** adjacent Y values, then the percentile(Y,P) function returns
4483
-** the height of the graph at P*(N-1)/100.
4484
-**
4485
-** (11) The percentile(Y,P) function always returns either a floating
4486
-** point number or NULL.
4487
-**
4488
-** (12) The percentile(Y,P) is implemented as a single C99 source-code
4489
-** file that compiles into a shared-library or DLL that can be loaded
4490
-** into SQLite using the sqlite3_load_extension() interface.
4491
-**
4492
-** (13) A separate median(Y) function is the equivalent percentile(Y,50).
4493
-**
4494
-** (14) A separate percentile_cont(Y,P) function is equivalent to
4495
-** percentile(Y,P/100.0). In other words, the fraction value in
4496
-** the second argument is in the range of 0 to 1 instead of 0 to 100.
4497
-**
4498
-** (15) A separate percentile_disc(Y,P) function is like
4499
-** percentile_cont(Y,P) except that instead of returning the weighted
4500
-** average of the nearest two input values, it returns the next lower
4501
-** value. So the percentile_disc(Y,P) will always return a value
4502
-** that was one of the inputs.
4503
-**
4504
-** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
4505
-** percentile_disc(Y,P) can be used as window functions.
4506
-**
4507
-** Differences from standard SQL:
4508
-**
4509
-** * The percentile_cont(X,P) function is equivalent to the following in
4510
-** standard SQL:
4511
-**
4512
-** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
4513
-**
4514
-** The SQLite syntax is much more compact. The standard SQL syntax
4515
-** is also supported if SQLite is compiled with the
4516
-** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
4517
-**
4518
-** * No median(X) function exists in the SQL standard. App developers
4519
-** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
4520
-**
4521
-** * No percentile(Y,P) function exists in the SQL standard. Instead of
4522
-** percential(Y,P), developers must write this:
4523
-** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
4524
-** the fraction parameter to percentile() goes from 0 to 100 whereas
4525
-** the fraction parameter in SQL standard percentile_cont() goes from
4526
-** 0 to 1.
4527
-**
4528
-** Implementation notes as of 2024-08-31:
4529
-**
4530
-** * The regular aggregate-function versions of these routines work
4531
-** by accumulating all values in an array of doubles, then sorting
4532
-** that array using quicksort before computing the answer. Thus
4533
-** the runtime is O(NlogN) where N is the number of rows of input.
4534
-**
4535
-** * For the window-function versions of these routines, the array of
4536
-** inputs is sorted as soon as the first value is computed. Thereafter,
4537
-** the array is kept in sorted order using an insert-sort. This
4538
-** results in O(N*K) performance where K is the size of the window.
4539
-** One can imagine alternative implementations that give O(N*logN*logK)
4540
-** performance, but they require more complex logic and data structures.
4541
-** The developers have elected to keep the asymptotically slower
4542
-** algorithm for now, for simplicity, under the theory that window
4543
-** functions are seldom used and when they are, the window size K is
4544
-** often small. The developers might revisit that decision later,
4545
-** should the need arise.
4546
-*/
4547
-#if defined(SQLITE3_H)
4548
- /* no-op */
4549
-#elif defined(SQLITE_STATIC_PERCENTILE)
4550
-/* # include "sqlite3.h" */
4551
-#else
4552
-/* # include "sqlite3ext.h" */
4553
- SQLITE_EXTENSION_INIT1
4554
-#endif
4555
-#include <assert.h>
4556
-#include <string.h>
4557
-#include <stdlib.h>
4558
-
4559
-/* The following object is the group context for a single percentile()
4560
-** aggregate. Remember all input Y values until the very end.
4561
-** Those values are accumulated in the Percentile.a[] array.
4562
-*/
4563
-typedef struct Percentile Percentile;
4564
-struct Percentile {
4565
- unsigned nAlloc; /* Number of slots allocated for a[] */
4566
- unsigned nUsed; /* Number of slots actually used in a[] */
4567
- char bSorted; /* True if a[] is already in sorted order */
4568
- char bKeepSorted; /* True if advantageous to keep a[] sorted */
4569
- char bPctValid; /* True if rPct is valid */
4570
- double rPct; /* Fraction. 0.0 to 1.0 */
4571
- double *a; /* Array of Y values */
4572
-};
4573
-
4574
-/* Details of each function in the percentile family */
4575
-typedef struct PercentileFunc PercentileFunc;
4576
-struct PercentileFunc {
4577
- const char *zName; /* Function name */
4578
- char nArg; /* Number of arguments */
4579
- char mxFrac; /* Maximum value of the "fraction" input */
4580
- char bDiscrete; /* True for percentile_disc() */
4581
-};
4582
-static const PercentileFunc aPercentFunc[] = {
4583
- { "median", 1, 1, 0 },
4584
- { "percentile", 2, 100, 0 },
4585
- { "percentile_cont", 2, 1, 0 },
4586
- { "percentile_disc", 2, 1, 1 },
4587
-};
4588
-
4589
-/*
4590
-** Return TRUE if the input floating-point number is an infinity.
4591
-*/
4592
-static int percentIsInfinity(double r){
4593
- sqlite3_uint64 u;
4594
- assert( sizeof(u)==sizeof(r) );
4595
- memcpy(&u, &r, sizeof(u));
4596
- return ((u>>52)&0x7ff)==0x7ff;
4597
-}
4598
-
4599
-/*
4600
-** Return TRUE if two doubles differ by 0.001 or less.
4601
-*/
4602
-static int percentSameValue(double a, double b){
4603
- a -= b;
4604
- return a>=-0.001 && a<=0.001;
4605
-}
4606
-
4607
-/*
4608
-** Search p (which must have p->bSorted) looking for an entry with
4609
-** value y. Return the index of that entry.
4610
-**
4611
-** If bExact is true, return -1 if the entry is not found.
4612
-**
4613
-** If bExact is false, return the index at which a new entry with
4614
-** value y should be insert in order to keep the values in sorted
4615
-** order. The smallest return value in this case will be 0, and
4616
-** the largest return value will be p->nUsed.
4617
-*/
4618
-static int percentBinarySearch(Percentile *p, double y, int bExact){
4619
- int iFirst = 0; /* First element of search range */
4620
- int iLast = p->nUsed - 1; /* Last element of search range */
4621
- while( iLast>=iFirst ){
4622
- int iMid = (iFirst+iLast)/2;
4623
- double x = p->a[iMid];
4624
- if( x<y ){
4625
- iFirst = iMid + 1;
4626
- }else if( x>y ){
4627
- iLast = iMid - 1;
4628
- }else{
4629
- return iMid;
4630
- }
4631
- }
4632
- if( bExact ) return -1;
4633
- return iFirst;
4634
-}
4635
-
4636
-/*
4637
-** Generate an error for a percentile function.
4638
-**
4639
-** The error format string must have exactly one occurrence of "%%s()"
4640
-** (with two '%' characters). That substring will be replaced by the name
4641
-** of the function.
4642
-*/
4643
-static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
4644
- PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
4645
- char *zMsg1;
4646
- char *zMsg2;
4647
- va_list ap;
4648
-
4649
- va_start(ap, zFormat);
4650
- zMsg1 = sqlite3_vmprintf(zFormat, ap);
4651
- va_end(ap);
4652
- zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0;
4653
- sqlite3_result_error(pCtx, zMsg2, -1);
4654
- sqlite3_free(zMsg1);
4655
- sqlite3_free(zMsg2);
4656
-}
4657
-
4658
-/*
4659
-** The "step" function for percentile(Y,P) is called once for each
4660
-** input row.
4661
-*/
4662
-static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
4663
- Percentile *p;
4664
- double rPct;
4665
- int eType;
4666
- double y;
4667
- assert( argc==2 || argc==1 );
4668
-
4669
- if( argc==1 ){
4670
- /* Requirement 13: median(Y) is the same as percentile(Y,50). */
4671
- rPct = 0.5;
4672
- }else{
4673
- /* Requirement 3: P must be a number between 0 and 100 */
4674
- PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
4675
- eType = sqlite3_value_numeric_type(argv[1]);
4676
- rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac;
4677
- if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
4678
- || rPct<0.0 || rPct>1.0
4679
- ){
4680
- percentError(pCtx, "the fraction argument to %%s()"
4681
- " is not between 0.0 and %.1f",
4682
- (double)pFunc->mxFrac);
4683
- return;
4684
- }
4685
- }
4686
-
4687
- /* Allocate the session context. */
4688
- p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
4689
- if( p==0 ) return;
4690
-
4691
- /* Remember the P value. Throw an error if the P value is different
4692
- ** from any prior row, per Requirement (2). */
4693
- if( !p->bPctValid ){
4694
- p->rPct = rPct;
4695
- p->bPctValid = 1;
4696
- }else if( !percentSameValue(p->rPct,rPct) ){
4697
- percentError(pCtx, "the fraction argument to %%s()"
4698
- " is not the same for all input rows");
4699
- return;
4700
- }
4701
-
4702
- /* Ignore rows for which Y is NULL */
4703
- eType = sqlite3_value_type(argv[0]);
4704
- if( eType==SQLITE_NULL ) return;
4705
-
4706
- /* If not NULL, then Y must be numeric. Otherwise throw an error.
4707
- ** Requirement 4 */
4708
- if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
4709
- percentError(pCtx, "input to %%s() is not numeric");
4710
- return;
4711
- }
4712
-
4713
- /* Throw an error if the Y value is infinity or NaN */
4714
- y = sqlite3_value_double(argv[0]);
4715
- if( percentIsInfinity(y) ){
4716
- percentError(pCtx, "Inf input to %%s()");
4717
- return;
4718
- }
4719
-
4720
- /* Allocate and store the Y */
4721
- if( p->nUsed>=p->nAlloc ){
4722
- unsigned n = p->nAlloc*2 + 250;
4723
- double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
4724
- if( a==0 ){
4725
- sqlite3_free(p->a);
4726
- memset(p, 0, sizeof(*p));
4727
- sqlite3_result_error_nomem(pCtx);
4728
- return;
4729
- }
4730
- p->nAlloc = n;
4731
- p->a = a;
4732
- }
4733
- if( p->nUsed==0 ){
4734
- p->a[p->nUsed++] = y;
4735
- p->bSorted = 1;
4736
- }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
4737
- p->a[p->nUsed++] = y;
4738
- }else if( p->bKeepSorted ){
4739
- int i;
4740
- i = percentBinarySearch(p, y, 0);
4741
- if( i<(int)p->nUsed ){
4742
- memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
4743
- }
4744
- p->a[i] = y;
4745
- p->nUsed++;
4746
- }else{
4747
- p->a[p->nUsed++] = y;
4748
- p->bSorted = 0;
4749
- }
4750
-}
4751
-
4752
-/*
4753
-** Interchange two doubles.
4754
-*/
4755
-#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
4756
-
4757
-/*
4758
-** Sort an array of doubles.
4759
-**
4760
-** Algorithm: quicksort
4761
-**
4762
-** This is implemented separately rather than using the qsort() routine
4763
-** from the standard library because:
4764
-**
4765
-** (1) To avoid a dependency on qsort()
4766
-** (2) To avoid the function call to the comparison routine for each
4767
-** comparison.
4768
-*/
4769
-static void percentSort(double *a, unsigned int n){
4770
- int iLt; /* Entries before a[iLt] are less than rPivot */
4771
- int iGt; /* Entries at or after a[iGt] are greater than rPivot */
4772
- int i; /* Loop counter */
4773
- double rPivot; /* The pivot value */
4774
-
4775
- assert( n>=2 );
4776
- if( a[0]>a[n-1] ){
4777
- SWAP_DOUBLE(a[0],a[n-1])
4778
- }
4779
- if( n==2 ) return;
4780
- iGt = n-1;
4781
- i = n/2;
4782
- if( a[0]>a[i] ){
4783
- SWAP_DOUBLE(a[0],a[i])
4784
- }else if( a[i]>a[iGt] ){
4785
- SWAP_DOUBLE(a[i],a[iGt])
4786
- }
4787
- if( n==3 ) return;
4788
- rPivot = a[i];
4789
- iLt = i = 1;
4790
- do{
4791
- if( a[i]<rPivot ){
4792
- if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
4793
- iLt++;
4794
- i++;
4795
- }else if( a[i]>rPivot ){
4796
- do{
4797
- iGt--;
4798
- }while( iGt>i && a[iGt]>rPivot );
4799
- SWAP_DOUBLE(a[i],a[iGt])
4800
- }else{
4801
- i++;
4802
- }
4803
- }while( i<iGt );
4804
- if( iLt>=2 ) percentSort(a, iLt);
4805
- if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
4806
-
4807
-/* Uncomment for testing */
4808
-#if 0
4809
- for(i=0; i<n-1; i++){
4810
- assert( a[i]<=a[i+1] );
4811
- }
4812
-#endif
4813
-}
4814
-
4815
-
4816
-/*
4817
-** The "inverse" function for percentile(Y,P) is called to remove a
4818
-** row that was previously inserted by "step".
4819
-*/
4820
-static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
4821
- Percentile *p;
4822
- int eType;
4823
- double y;
4824
- int i;
4825
- assert( argc==2 || argc==1 );
4826
-
4827
- /* Allocate the session context. */
4828
- p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
4829
- assert( p!=0 );
4830
-
4831
- /* Ignore rows for which Y is NULL */
4832
- eType = sqlite3_value_type(argv[0]);
4833
- if( eType==SQLITE_NULL ) return;
4834
-
4835
- /* If not NULL, then Y must be numeric. Otherwise throw an error.
4836
- ** Requirement 4 */
4837
- if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
4838
- return;
4839
- }
4840
-
4841
- /* Ignore the Y value if it is infinity or NaN */
4842
- y = sqlite3_value_double(argv[0]);
4843
- if( percentIsInfinity(y) ){
4844
- return;
4845
- }
4846
- if( p->bSorted==0 ){
4847
- assert( p->nUsed>1 );
4848
- percentSort(p->a, p->nUsed);
4849
- p->bSorted = 1;
4850
- }
4851
- p->bKeepSorted = 1;
4852
-
4853
- /* Find and remove the row */
4854
- i = percentBinarySearch(p, y, 1);
4855
- if( i>=0 ){
4856
- p->nUsed--;
4857
- if( i<(int)p->nUsed ){
4858
- memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
4859
- }
4860
- }
4861
-}
4862
-
4863
-/*
4864
-** Compute the final output of percentile(). Clean up all allocated
4865
-** memory if and only if bIsFinal is true.
4866
-*/
4867
-static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
4868
- Percentile *p;
4869
- PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
4870
- unsigned i1, i2;
4871
- double v1, v2;
4872
- double ix, vx;
4873
- p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
4874
- if( p==0 ) return;
4875
- if( p->a==0 ) return;
4876
- if( p->nUsed ){
4877
- if( p->bSorted==0 ){
4878
- assert( p->nUsed>1 );
4879
- percentSort(p->a, p->nUsed);
4880
- p->bSorted = 1;
4881
- }
4882
- ix = p->rPct*(p->nUsed-1);
4883
- i1 = (unsigned)ix;
4884
- if( pFunc->bDiscrete ){
4885
- vx = p->a[i1];
4886
- }else{
4887
- i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
4888
- v1 = p->a[i1];
4889
- v2 = p->a[i2];
4890
- vx = v1 + (v2-v1)*(ix-i1);
4891
- }
4892
- sqlite3_result_double(pCtx, vx);
4893
- }
4894
- if( bIsFinal ){
4895
- sqlite3_free(p->a);
4896
- memset(p, 0, sizeof(*p));
4897
- }else{
4898
- p->bKeepSorted = 1;
4899
- }
4900
-}
4901
-static void percentFinal(sqlite3_context *pCtx){
4902
- percentCompute(pCtx, 1);
4903
-}
4904
-static void percentValue(sqlite3_context *pCtx){
4905
- percentCompute(pCtx, 0);
4906
-}
4907
-
4908
-#if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE)
4909
-
4910
-#endif
4911
-int sqlite3_percentile_init(
4912
- sqlite3 *db,
4913
- char **pzErrMsg,
4914
- const sqlite3_api_routines *pApi
4915
-){
4916
- int rc = SQLITE_OK;
4917
- unsigned int i;
4918
-#ifdef SQLITE3EXT_H
4919
- SQLITE_EXTENSION_INIT2(pApi);
4920
-#else
4921
- (void)pApi; /* Unused parameter */
4922
-#endif
4923
- (void)pzErrMsg; /* Unused parameter */
4924
- for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
4925
- rc = sqlite3_create_window_function(db,
4926
- aPercentFunc[i].zName,
4927
- aPercentFunc[i].nArg,
4928
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
4929
- (void*)&aPercentFunc[i],
4930
- percentStep, percentFinal, percentValue, percentInverse, 0);
4931
- if( rc ) break;
4932
- }
4933
- return rc;
4934
-}
4935
-
4936
-/************************* End ../ext/misc/percentile.c ********************/
49374503
#undef sqlite3_base_init
49384504
#define sqlite3_base_init sqlite3_base64_init
49394505
/************************* Begin ../ext/misc/base64.c ******************/
49404506
/*
49414507
** 2022-11-18
@@ -5144,20 +4710,22 @@
51444710
return pOut;
51454711
}
51464712
51474713
/* This function does the work for the SQLite base64(x) UDF. */
51484714
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
5149
- int nb, nc, nv = sqlite3_value_bytes(av[0]);
4715
+ sqlite3_int64 nb;
4716
+ sqlite3_int64 nv = sqlite3_value_bytes(av[0]);
4717
+ sqlite3_int64 nc;
51504718
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
51514719
SQLITE_LIMIT_LENGTH, -1);
51524720
char *cBuf;
51534721
u8 *bBuf;
51544722
assert(na==1);
51554723
switch( sqlite3_value_type(av[0]) ){
51564724
case SQLITE_BLOB:
51574725
nb = nv;
5158
- nc = 4*(nv+2/3); /* quads needed */
4726
+ nc = 4*((nv+2)/3); /* quads needed */
51594727
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
51604728
if( nvMax < nc ){
51614729
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
51624730
return;
51634731
}
@@ -5524,11 +5092,11 @@
55245092
}
55255093
# endif
55265094
55275095
/* This function does the work for the SQLite base85(x) UDF. */
55285096
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
5529
- int nb, nc, nv = sqlite3_value_bytes(av[0]);
5097
+ sqlite3_int64 nb, nc, nv = sqlite3_value_bytes(av[0]);
55305098
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
55315099
SQLITE_LIMIT_LENGTH, -1);
55325100
char *cBuf;
55335101
u8 *bBuf;
55345102
assert(na==1);
@@ -5829,10 +5397,13 @@
58295397
}
58305398
memcpy(&a,&r,sizeof(a));
58315399
if( a==0 ){
58325400
e = 0;
58335401
m = 0;
5402
+ }else if( a==(sqlite3_int64)0x8000000000000000LL ){
5403
+ e = -1996;
5404
+ m = -1;
58345405
}else{
58355406
e = a>>52;
58365407
m = a & ((((sqlite3_int64)1)<<52)-1);
58375408
if( e==0 ){
58385409
m <<= 1;
@@ -6052,23 +5623,24 @@
60525623
** Examples:
60535624
**
60545625
** SELECT * FROM generate_series(0,100,5);
60555626
**
60565627
** The query above returns integers from 0 through 100 counting by steps
6057
-** of 5.
5628
+** of 5. In other words, 0, 5, 10, 15, ..., 90, 95, 100. There are a total
5629
+** of 21 rows.
60585630
**
60595631
** SELECT * FROM generate_series(0,100);
60605632
**
6061
-** Integers from 0 through 100 with a step size of 1.
5633
+** Integers from 0 through 100 with a step size of 1. 101 rows.
60625634
**
60635635
** SELECT * FROM generate_series(20) LIMIT 10;
60645636
**
6065
-** Integers 20 through 29.
5637
+** Integers 20 through 29. 10 rows.
60665638
**
60675639
** SELECT * FROM generate_series(0,-100,-5);
60685640
**
6069
-** Integers 0 -5 -10 ... -100.
5641
+** Integers 0 -5 -10 ... -100. 21 rows.
60705642
**
60715643
** SELECT * FROM generate_series(0,-1);
60725644
**
60735645
** Empty sequence.
60745646
**
@@ -6140,143 +5712,92 @@
61405712
#include <string.h>
61415713
#include <limits.h>
61425714
#include <math.h>
61435715
61445716
#ifndef SQLITE_OMIT_VIRTUALTABLE
6145
-/*
6146
-** Return that member of a generate_series(...) sequence whose 0-based
6147
-** index is ix. The 0th member is given by smBase. The sequence members
6148
-** progress per ix increment by smStep.
6149
-*/
6150
-static sqlite3_int64 genSeqMember(
6151
- sqlite3_int64 smBase,
6152
- sqlite3_int64 smStep,
6153
- sqlite3_uint64 ix
6154
-){
6155
- static const sqlite3_uint64 mxI64 =
6156
- ((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
6157
- if( ix>=mxI64 ){
6158
- /* Get ix into signed i64 range. */
6159
- ix -= mxI64;
6160
- /* With 2's complement ALU, this next can be 1 step, but is split into
6161
- * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
6162
- smBase += (mxI64/2) * smStep;
6163
- smBase += (mxI64 - mxI64/2) * smStep;
6164
- }
6165
- /* Under UBSAN (or on 1's complement machines), must do this last term
6166
- * in steps to avoid the dreaded (and harmless) signed multiply overflow. */
6167
- if( ix>=2 ){
6168
- sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
6169
- smBase += ix2*smStep;
6170
- ix -= ix2;
6171
- }
6172
- return smBase + ((sqlite3_int64)ix)*smStep;
6173
-}
6174
-
6175
-/* typedef unsigned char u8; */
6176
-
6177
-typedef struct SequenceSpec {
6178
- sqlite3_int64 iOBase; /* Original starting value ("start") */
6179
- sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
6180
- sqlite3_int64 iBase; /* Starting value to actually use */
6181
- sqlite3_int64 iTerm; /* Terminal value to actually use */
6182
- sqlite3_int64 iStep; /* Increment ("step") */
6183
- sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
6184
- sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
6185
- sqlite3_int64 iValueNow; /* Current value during generation */
6186
- u8 isNotEOF; /* Sequence generation not exhausted */
6187
- u8 isReversing; /* Sequence is being reverse generated */
6188
-} SequenceSpec;
6189
-
6190
-/*
6191
-** Prepare a SequenceSpec for use in generating an integer series
6192
-** given initialized iBase, iTerm and iStep values. Sequence is
6193
-** initialized per given isReversing. Other members are computed.
6194
-*/
6195
-static void setupSequence( SequenceSpec *pss ){
6196
- int bSameSigns;
6197
- pss->uSeqIndexMax = 0;
6198
- pss->isNotEOF = 0;
6199
- bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
6200
- if( pss->iTerm < pss->iBase ){
6201
- sqlite3_uint64 nuspan = 0;
6202
- if( bSameSigns ){
6203
- nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
6204
- }else{
6205
- /* Under UBSAN (or on 1's complement machines), must do this in steps.
6206
- * In this clause, iBase>=0 and iTerm<0 . */
6207
- nuspan = 1;
6208
- nuspan += pss->iBase;
6209
- nuspan += -(pss->iTerm+1);
6210
- }
6211
- if( pss->iStep<0 ){
6212
- pss->isNotEOF = 1;
6213
- if( nuspan==ULONG_MAX ){
6214
- pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1;
6215
- }else if( pss->iStep>LLONG_MIN ){
6216
- pss->uSeqIndexMax = nuspan/-pss->iStep;
6217
- }
6218
- }
6219
- }else if( pss->iTerm > pss->iBase ){
6220
- sqlite3_uint64 puspan = 0;
6221
- if( bSameSigns ){
6222
- puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
6223
- }else{
6224
- /* Under UBSAN (or on 1's complement machines), must do this in steps.
6225
- * In this clause, iTerm>=0 and iBase<0 . */
6226
- puspan = 1;
6227
- puspan += pss->iTerm;
6228
- puspan += -(pss->iBase+1);
6229
- }
6230
- if( pss->iStep>0 ){
6231
- pss->isNotEOF = 1;
6232
- pss->uSeqIndexMax = puspan/pss->iStep;
6233
- }
6234
- }else if( pss->iTerm == pss->iBase ){
6235
- pss->isNotEOF = 1;
6236
- pss->uSeqIndexMax = 0;
6237
- }
6238
- pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0;
6239
- pss->iValueNow = (pss->isReversing)
6240
- ? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax)
6241
- : pss->iBase;
6242
-}
6243
-
6244
-/*
6245
-** Progress sequence generator to yield next value, if any.
6246
-** Leave its state to either yield next value or be at EOF.
6247
-** Return whether there is a next value, or 0 at EOF.
6248
-*/
6249
-static int progressSequence( SequenceSpec *pss ){
6250
- if( !pss->isNotEOF ) return 0;
6251
- if( pss->isReversing ){
6252
- if( pss->uSeqIndexNow > 0 ){
6253
- pss->uSeqIndexNow--;
6254
- pss->iValueNow -= pss->iStep;
6255
- }else{
6256
- pss->isNotEOF = 0;
6257
- }
6258
- }else{
6259
- if( pss->uSeqIndexNow < pss->uSeqIndexMax ){
6260
- pss->uSeqIndexNow++;
6261
- pss->iValueNow += pss->iStep;
6262
- }else{
6263
- pss->isNotEOF = 0;
6264
- }
6265
- }
6266
- return pss->isNotEOF;
6267
-}
62685717
62695718
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
62705719
** serve as the underlying representation of a cursor that scans
6271
-** over rows of the result
5720
+** over rows of the result.
5721
+**
5722
+** iOBase, iOTerm, and iOStep are the original values of the
5723
+** start=, stop=, and step= constraints on the query. These are
5724
+** the values reported by the start, stop, and step columns of the
5725
+** virtual table.
5726
+**
5727
+** iBase, iTerm, iStep, and bDescp are the actual values used to generate
5728
+** the sequence. These might be different from the iOxxxx values.
5729
+** For example in
5730
+**
5731
+** SELECT value FROM generate_series(1,11,2)
5732
+** WHERE value BETWEEN 4 AND 8;
5733
+**
5734
+** The iOBase is 1, but the iBase is 5. iOTerm is 11 but iTerm is 7.
5735
+** Another example:
5736
+**
5737
+** SELECT value FROM generate_series(1,15,3) ORDER BY value DESC;
5738
+**
5739
+** The cursor initialization for the above query is:
5740
+**
5741
+** iOBase = 1 iBase = 13
5742
+** iOTerm = 15 iTerm = 1
5743
+** iOStep = 3 iStep = 3 bDesc = 1
5744
+**
5745
+** The actual step size is unsigned so that can have a value of
5746
+** +9223372036854775808 which is needed for querys like this:
5747
+**
5748
+** SELECT value
5749
+** FROM generate_series(9223372036854775807,
5750
+** -9223372036854775808,
5751
+** -9223372036854775808)
5752
+** ORDER BY value ASC;
5753
+**
5754
+** The setup for the previous query will be:
5755
+**
5756
+** iOBase = 9223372036854775807 iBase = -1
5757
+** iOTerm = -9223372036854775808 iTerm = 9223372036854775807
5758
+** iOStep = -9223372036854775808 iStep = 9223372036854775808 bDesc = 0
62725759
*/
5760
+/* typedef unsigned char u8; */
62735761
typedef struct series_cursor series_cursor;
62745762
struct series_cursor {
62755763
sqlite3_vtab_cursor base; /* Base class - must be first */
6276
- SequenceSpec ss; /* (this) Derived class data */
5764
+ sqlite3_int64 iOBase; /* Original starting value ("start") */
5765
+ sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
5766
+ sqlite3_int64 iOStep; /* Original step value */
5767
+ sqlite3_int64 iBase; /* Starting value to actually use */
5768
+ sqlite3_int64 iTerm; /* Terminal value to actually use */
5769
+ sqlite3_uint64 iStep; /* The step size */
5770
+ sqlite3_int64 iValue; /* Current value */
5771
+ u8 bDesc; /* iStep is really negative */
5772
+ u8 bDone; /* True if stepped past last element */
62775773
};
5774
+
5775
+/*
5776
+** Computed the difference between two 64-bit signed integers using a
5777
+** convoluted computation designed to work around the silly restriction
5778
+** against signed integer overflow in C.
5779
+*/
5780
+static sqlite3_uint64 span64(sqlite3_int64 a, sqlite3_int64 b){
5781
+ assert( a>=b );
5782
+ return (*(sqlite3_uint64*)&a) - (*(sqlite3_uint64*)&b);
5783
+}
5784
+
5785
+/*
5786
+** Add or substract an unsigned 64-bit integer from a signed 64-bit integer
5787
+** and return the new signed 64-bit integer.
5788
+*/
5789
+static sqlite3_int64 add64(sqlite3_int64 a, sqlite3_uint64 b){
5790
+ sqlite3_uint64 x = *(sqlite3_uint64*)&a;
5791
+ x += b;
5792
+ return *(sqlite3_int64*)&x;
5793
+}
5794
+static sqlite3_int64 sub64(sqlite3_int64 a, sqlite3_uint64 b){
5795
+ sqlite3_uint64 x = *(sqlite3_uint64*)&a;
5796
+ x -= b;
5797
+ return *(sqlite3_int64*)&x;
5798
+}
62785799
62795800
/*
62805801
** The seriesConnect() method is invoked to create a new
62815802
** series_vtab that describes the generate_series virtual table.
62825803
**
@@ -6354,11 +5875,19 @@
63545875
/*
63555876
** Advance a series_cursor to its next row of output.
63565877
*/
63575878
static int seriesNext(sqlite3_vtab_cursor *cur){
63585879
series_cursor *pCur = (series_cursor*)cur;
6359
- progressSequence( & pCur->ss );
5880
+ if( pCur->iValue==pCur->iTerm ){
5881
+ pCur->bDone = 1;
5882
+ }else if( pCur->bDesc ){
5883
+ pCur->iValue = sub64(pCur->iValue, pCur->iStep);
5884
+ assert( pCur->iValue>=pCur->iTerm );
5885
+ }else{
5886
+ pCur->iValue = add64(pCur->iValue, pCur->iStep);
5887
+ assert( pCur->iValue<=pCur->iTerm );
5888
+ }
63605889
return SQLITE_OK;
63615890
}
63625891
63635892
/*
63645893
** Return values of columns for the row at which the series_cursor
@@ -6370,41 +5899,41 @@
63705899
int i /* Which column to return */
63715900
){
63725901
series_cursor *pCur = (series_cursor*)cur;
63735902
sqlite3_int64 x = 0;
63745903
switch( i ){
6375
- case SERIES_COLUMN_START: x = pCur->ss.iOBase; break;
6376
- case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break;
6377
- case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
6378
- default: x = pCur->ss.iValueNow; break;
5904
+ case SERIES_COLUMN_START: x = pCur->iOBase; break;
5905
+ case SERIES_COLUMN_STOP: x = pCur->iOTerm; break;
5906
+ case SERIES_COLUMN_STEP: x = pCur->iOStep; break;
5907
+ default: x = pCur->iValue; break;
63795908
}
63805909
sqlite3_result_int64(ctx, x);
63815910
return SQLITE_OK;
63825911
}
63835912
63845913
#ifndef LARGEST_UINT64
6385
-#define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
6386
-#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
6387
-#define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
5914
+#define LARGEST_INT64 ((sqlite3_int64)0x7fffffffffffffffLL)
5915
+#define LARGEST_UINT64 ((sqlite3_uint64)0xffffffffffffffffULL)
5916
+#define SMALLEST_INT64 ((sqlite3_int64)0x8000000000000000LL)
63885917
#endif
63895918
63905919
/*
63915920
** The rowid is the same as the value.
63925921
*/
63935922
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
63945923
series_cursor *pCur = (series_cursor*)cur;
6395
- *pRowid = pCur->ss.iValueNow;
5924
+ *pRowid = pCur->iValue;
63965925
return SQLITE_OK;
63975926
}
63985927
63995928
/*
64005929
** Return TRUE if the cursor has been moved off of the last
64015930
** row of output.
64025931
*/
64035932
static int seriesEof(sqlite3_vtab_cursor *cur){
64045933
series_cursor *pCur = (series_cursor*)cur;
6405
- return !pCur->ss.isNotEOF;
5934
+ return pCur->bDone;
64065935
}
64075936
64085937
/* True to cause run-time checking of the start=, stop=, and/or step=
64095938
** parameters. The only reason to do this is for testing the
64105939
** constraint checking logic for virtual tables in the SQLite core.
@@ -6411,10 +5940,63 @@
64115940
*/
64125941
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
64135942
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
64145943
#endif
64155944
5945
+/*
5946
+** Return the number of steps between pCur->iBase and pCur->iTerm if
5947
+** the step width is pCur->iStep.
5948
+*/
5949
+static sqlite3_uint64 seriesSteps(series_cursor *pCur){
5950
+ if( pCur->bDesc ){
5951
+ assert( pCur->iBase >= pCur->iTerm );
5952
+ return span64(pCur->iBase, pCur->iTerm)/pCur->iStep;
5953
+ }else{
5954
+ assert( pCur->iBase <= pCur->iTerm );
5955
+ return span64(pCur->iTerm, pCur->iBase)/pCur->iStep;
5956
+ }
5957
+}
5958
+
5959
+#if defined(SQLITE_ENABLE_MATH_FUNCTIONS) || defined(_WIN32)
5960
+/*
5961
+** Case 1 (the most common case):
5962
+** The standard math library is available so use ceil() and floor() from there.
5963
+*/
5964
+static double seriesCeil(double r){ return ceil(r); }
5965
+static double seriesFloor(double r){ return floor(r); }
5966
+#elif defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
5967
+/*
5968
+** Case 2 (2nd most common): Use GCC/Clang builtins
5969
+*/
5970
+static double seriesCeil(double r){ return __builtin_ceil(r); }
5971
+static double seriesFloor(double r){ return __builtin_floor(r); }
5972
+#else
5973
+/*
5974
+** Case 3 (rarely happens): Use home-grown ceil() and floor() routines.
5975
+*/
5976
+static double seriesCeil(double r){
5977
+ sqlite3_int64 x;
5978
+ if( r!=r ) return r;
5979
+ if( r<=(-4503599627370496.0) ) return r;
5980
+ if( r>=(+4503599627370496.0) ) return r;
5981
+ x = (sqlite3_int64)r;
5982
+ if( r==(double)x ) return r;
5983
+ if( r>(double)x ) x++;
5984
+ return (double)x;
5985
+}
5986
+static double seriesFloor(double r){
5987
+ sqlite3_int64 x;
5988
+ if( r!=r ) return r;
5989
+ if( r<=(-4503599627370496.0) ) return r;
5990
+ if( r>=(+4503599627370496.0) ) return r;
5991
+ x = (sqlite3_int64)r;
5992
+ if( r==(double)x ) return r;
5993
+ if( r<(double)x ) x--;
5994
+ return (double)x;
5995
+}
5996
+#endif
5997
+
64165998
/*
64175999
** This method is called to "rewind" the series_cursor object back
64186000
** to the first row of output. This method is always called at least
64196001
** once prior to any call to seriesColumn() or seriesRowid() or
64206002
** seriesEof().
@@ -6444,185 +6026,243 @@
64446026
sqlite3_vtab_cursor *pVtabCursor,
64456027
int idxNum, const char *idxStrUnused,
64466028
int argc, sqlite3_value **argv
64476029
){
64486030
series_cursor *pCur = (series_cursor *)pVtabCursor;
6449
- int i = 0;
6450
- int returnNoRows = 0;
6451
- sqlite3_int64 iMin = SMALLEST_INT64;
6452
- sqlite3_int64 iMax = LARGEST_INT64;
6453
- sqlite3_int64 iLimit = 0;
6454
- sqlite3_int64 iOffset = 0;
6031
+ int iArg = 0; /* Arguments used so far */
6032
+ int i; /* Loop counter */
6033
+ sqlite3_int64 iMin = SMALLEST_INT64; /* Smallest allowed output value */
6034
+ sqlite3_int64 iMax = LARGEST_INT64; /* Largest allowed output value */
6035
+ sqlite3_int64 iLimit = 0; /* if >0, the value of the LIMIT */
6036
+ sqlite3_int64 iOffset = 0; /* if >0, the value of the OFFSET */
64556037
64566038
(void)idxStrUnused;
6039
+
6040
+ /* If any constraints have a NULL value, then return no rows.
6041
+ ** See ticket https://sqlite.org/src/info/fac496b61722daf2
6042
+ */
6043
+ for(i=0; i<argc; i++){
6044
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
6045
+ goto series_no_rows;
6046
+ }
6047
+ }
6048
+
6049
+ /* Capture the three HIDDEN parameters to the virtual table and insert
6050
+ ** default values for any parameters that are omitted.
6051
+ */
64576052
if( idxNum & 0x01 ){
6458
- pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
6053
+ pCur->iOBase = sqlite3_value_int64(argv[iArg++]);
64596054
}else{
6460
- pCur->ss.iBase = 0;
6055
+ pCur->iOBase = 0;
64616056
}
64626057
if( idxNum & 0x02 ){
6463
- pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
6058
+ pCur->iOTerm = sqlite3_value_int64(argv[iArg++]);
64646059
}else{
6465
- pCur->ss.iTerm = 0xffffffff;
6060
+ pCur->iOTerm = 0xffffffff;
64666061
}
64676062
if( idxNum & 0x04 ){
6468
- pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
6469
- if( pCur->ss.iStep==0 ){
6470
- pCur->ss.iStep = 1;
6471
- }else if( pCur->ss.iStep<0 ){
6472
- if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
6473
- }
6063
+ pCur->iOStep = sqlite3_value_int64(argv[iArg++]);
6064
+ if( pCur->iOStep==0 ) pCur->iOStep = 1;
64746065
}else{
6475
- pCur->ss.iStep = 1;
6066
+ pCur->iOStep = 1;
64766067
}
64776068
64786069
/* If there are constraints on the value column but there are
64796070
** no constraints on the start, stop, and step columns, then
64806071
** initialize the default range to be the entire range of 64-bit signed
64816072
** integers. This range will contracted by the value column constraints
64826073
** further below.
64836074
*/
64846075
if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
6485
- pCur->ss.iBase = SMALLEST_INT64;
6076
+ pCur->iOBase = SMALLEST_INT64;
64866077
}
64876078
if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
6488
- pCur->ss.iTerm = LARGEST_INT64;
6079
+ pCur->iOTerm = LARGEST_INT64;
64896080
}
6490
- pCur->ss.iOBase = pCur->ss.iBase;
6491
- pCur->ss.iOTerm = pCur->ss.iTerm;
6081
+ pCur->iBase = pCur->iOBase;
6082
+ pCur->iTerm = pCur->iOTerm;
6083
+ if( pCur->iOStep>0 ){
6084
+ pCur->iStep = pCur->iOStep;
6085
+ }else if( pCur->iOStep>SMALLEST_INT64 ){
6086
+ pCur->iStep = -pCur->iOStep;
6087
+ }else{
6088
+ pCur->iStep = LARGEST_INT64;
6089
+ pCur->iStep++;
6090
+ }
6091
+ pCur->bDesc = pCur->iOStep<0;
6092
+ if( pCur->bDesc==0 && pCur->iBase>pCur->iTerm ){
6093
+ goto series_no_rows;
6094
+ }
6095
+ if( pCur->bDesc!=0 && pCur->iBase<pCur->iTerm ){
6096
+ goto series_no_rows;
6097
+ }
64926098
64936099
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
64946100
** The range must first be constrained by the limits on value.
64956101
*/
64966102
if( idxNum & 0x20 ){
6497
- iLimit = sqlite3_value_int64(argv[i++]);
6103
+ iLimit = sqlite3_value_int64(argv[iArg++]);
64986104
if( idxNum & 0x40 ){
6499
- iOffset = sqlite3_value_int64(argv[i++]);
6105
+ iOffset = sqlite3_value_int64(argv[iArg++]);
65006106
}
65016107
}
65026108
6109
+ /* Narrow the range of iMin and iMax (the minimum and maximum outputs)
6110
+ ** based on equality and inequality constraints on the "value" column.
6111
+ */
65036112
if( idxNum & 0x3380 ){
6504
- /* Extract the maximum range of output values determined by
6505
- ** constraints on the "value" column.
6506
- */
6507
- if( idxNum & 0x0080 ){
6508
- if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6509
- double r = sqlite3_value_double(argv[i++]);
6510
- if( r==ceil(r) ){
6113
+ if( idxNum & 0x0080 ){ /* value=X */
6114
+ if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
6115
+ double r = sqlite3_value_double(argv[iArg++]);
6116
+ if( r==seriesCeil(r)
6117
+ && r>=(double)SMALLEST_INT64
6118
+ && r<=(double)LARGEST_INT64
6119
+ ){
65116120
iMin = iMax = (sqlite3_int64)r;
65126121
}else{
6513
- returnNoRows = 1;
6514
- }
6515
- }else{
6516
- iMin = iMax = sqlite3_value_int64(argv[i++]);
6517
- }
6518
- }else{
6519
- if( idxNum & 0x0300 ){
6520
- if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6521
- double r = sqlite3_value_double(argv[i++]);
6522
- if( idxNum & 0x0200 && r==ceil(r) ){
6523
- iMin = (sqlite3_int64)ceil(r+1.0);
6524
- }else{
6525
- iMin = (sqlite3_int64)ceil(r);
6526
- }
6527
- }else{
6528
- iMin = sqlite3_value_int64(argv[i++]);
6529
- if( idxNum & 0x0200 ){
6122
+ goto series_no_rows;
6123
+ }
6124
+ }else{
6125
+ iMin = iMax = sqlite3_value_int64(argv[iArg++]);
6126
+ }
6127
+ }else{
6128
+ if( idxNum & 0x0300 ){ /* value>X or value>=X */
6129
+ if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
6130
+ double r = sqlite3_value_double(argv[iArg++]);
6131
+ if( r<(double)SMALLEST_INT64 ){
6132
+ iMin = SMALLEST_INT64;
6133
+ }else if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){
6134
+ iMin = (sqlite3_int64)seriesCeil(r+1.0);
6135
+ }else{
6136
+ iMin = (sqlite3_int64)seriesCeil(r);
6137
+ }
6138
+ }else{
6139
+ iMin = sqlite3_value_int64(argv[iArg++]);
6140
+ if( (idxNum & 0x0200)!=0 ){
65306141
if( iMin==LARGEST_INT64 ){
6531
- returnNoRows = 1;
6142
+ goto series_no_rows;
65326143
}else{
65336144
iMin++;
65346145
}
65356146
}
65366147
}
65376148
}
6538
- if( idxNum & 0x3000 ){
6539
- if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6540
- double r = sqlite3_value_double(argv[i++]);
6541
- if( (idxNum & 0x2000)!=0 && r==floor(r) ){
6149
+ if( idxNum & 0x3000 ){ /* value<X or value<=X */
6150
+ if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
6151
+ double r = sqlite3_value_double(argv[iArg++]);
6152
+ if( r>(double)LARGEST_INT64 ){
6153
+ iMax = LARGEST_INT64;
6154
+ }else if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){
65426155
iMax = (sqlite3_int64)(r-1.0);
65436156
}else{
6544
- iMax = (sqlite3_int64)floor(r);
6157
+ iMax = (sqlite3_int64)seriesFloor(r);
65456158
}
65466159
}else{
6547
- iMax = sqlite3_value_int64(argv[i++]);
6160
+ iMax = sqlite3_value_int64(argv[iArg++]);
65486161
if( idxNum & 0x2000 ){
65496162
if( iMax==SMALLEST_INT64 ){
6550
- returnNoRows = 1;
6163
+ goto series_no_rows;
65516164
}else{
65526165
iMax--;
65536166
}
65546167
}
65556168
}
65566169
}
65576170
if( iMin>iMax ){
6558
- returnNoRows = 1;
6171
+ goto series_no_rows;
65596172
}
65606173
}
65616174
65626175
/* Try to reduce the range of values to be generated based on
65636176
** constraints on the "value" column.
65646177
*/
6565
- if( pCur->ss.iStep>0 ){
6566
- sqlite3_int64 szStep = pCur->ss.iStep;
6567
- if( pCur->ss.iBase<iMin ){
6568
- sqlite3_uint64 d = iMin - pCur->ss.iBase;
6569
- pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
6570
- }
6571
- if( pCur->ss.iTerm>iMax ){
6572
- pCur->ss.iTerm = iMax;
6178
+ if( pCur->bDesc==0 ){
6179
+ if( pCur->iBase<iMin ){
6180
+ sqlite3_uint64 span = span64(iMin,pCur->iBase);
6181
+ pCur->iBase = add64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
6182
+ if( pCur->iBase<iMin ){
6183
+ if( pCur->iBase > sub64(LARGEST_INT64, pCur->iStep) ){
6184
+ goto series_no_rows;
6185
+ }
6186
+ pCur->iBase = add64(pCur->iBase, pCur->iStep);
6187
+ }
6188
+ }
6189
+ if( pCur->iTerm>iMax ){
6190
+ pCur->iTerm = iMax;
65736191
}
65746192
}else{
6575
- sqlite3_int64 szStep = -pCur->ss.iStep;
6576
- assert( szStep>0 );
6577
- if( pCur->ss.iBase>iMax ){
6578
- sqlite3_uint64 d = pCur->ss.iBase - iMax;
6579
- pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
6580
- }
6581
- if( pCur->ss.iTerm<iMin ){
6582
- pCur->ss.iTerm = iMin;
6193
+ if( pCur->iBase>iMax ){
6194
+ sqlite3_uint64 span = span64(pCur->iBase,iMax);
6195
+ pCur->iBase = sub64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
6196
+ if( pCur->iBase>iMax ){
6197
+ if( pCur->iBase < add64(SMALLEST_INT64, pCur->iStep) ){
6198
+ goto series_no_rows;
6199
+ }
6200
+ pCur->iBase = sub64(pCur->iBase, pCur->iStep);
6201
+ }
6202
+ }
6203
+ if( pCur->iTerm<iMin ){
6204
+ pCur->iTerm = iMin;
65836205
}
65846206
}
65856207
}
6208
+
6209
+ /* Adjust iTerm so that it is exactly the last value of the series.
6210
+ */
6211
+ if( pCur->bDesc==0 ){
6212
+ if( pCur->iBase>pCur->iTerm ){
6213
+ goto series_no_rows;
6214
+ }
6215
+ pCur->iTerm = sub64(pCur->iTerm,
6216
+ span64(pCur->iTerm,pCur->iBase) % pCur->iStep);
6217
+ }else{
6218
+ if( pCur->iBase<pCur->iTerm ){
6219
+ goto series_no_rows;
6220
+ }
6221
+ pCur->iTerm = add64(pCur->iTerm,
6222
+ span64(pCur->iBase,pCur->iTerm) % pCur->iStep);
6223
+ }
6224
+
6225
+ /* Transform the series generator to output values in the requested
6226
+ ** order.
6227
+ */
6228
+ if( ((idxNum & 0x0008)!=0 && pCur->bDesc==0)
6229
+ || ((idxNum & 0x0010)!=0 && pCur->bDesc!=0)
6230
+ ){
6231
+ sqlite3_int64 tmp = pCur->iBase;
6232
+ pCur->iBase = pCur->iTerm;
6233
+ pCur->iTerm = tmp;
6234
+ pCur->bDesc = !pCur->bDesc;
6235
+ }
65866236
65876237
/* Apply LIMIT and OFFSET constraints, if any */
6238
+ assert( pCur->iStep!=0 );
65886239
if( idxNum & 0x20 ){
65896240
if( iOffset>0 ){
6590
- pCur->ss.iBase += pCur->ss.iStep*iOffset;
6591
- }
6592
- if( iLimit>=0 ){
6593
- sqlite3_int64 iTerm;
6594
- iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
6595
- if( pCur->ss.iStep<0 ){
6596
- if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
6597
- }else{
6598
- if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
6599
- }
6600
- }
6601
- }
6602
-
6603
-
6604
- for(i=0; i<argc; i++){
6605
- if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
6606
- /* If any of the constraints have a NULL value, then return no rows.
6607
- ** See ticket https://sqlite.org/src/info/fac496b61722daf2 */
6608
- returnNoRows = 1;
6609
- break;
6610
- }
6611
- }
6612
- if( returnNoRows ){
6613
- pCur->ss.iBase = 1;
6614
- pCur->ss.iTerm = 0;
6615
- pCur->ss.iStep = 1;
6616
- }
6617
- if( idxNum & 0x08 ){
6618
- pCur->ss.isReversing = pCur->ss.iStep > 0;
6619
- }else{
6620
- pCur->ss.isReversing = pCur->ss.iStep < 0;
6621
- }
6622
- setupSequence( &pCur->ss );
6241
+ if( seriesSteps(pCur) < (sqlite3_uint64)iOffset ){
6242
+ goto series_no_rows;
6243
+ }else if( pCur->bDesc ){
6244
+ pCur->iBase = sub64(pCur->iBase, pCur->iStep*iOffset);
6245
+ }else{
6246
+ pCur->iBase = add64(pCur->iBase, pCur->iStep*iOffset);
6247
+ }
6248
+ }
6249
+ if( iLimit>=0 && seriesSteps(pCur) > (sqlite3_uint64)iLimit ){
6250
+ pCur->iTerm = add64(pCur->iBase, (iLimit - 1)*pCur->iStep);
6251
+ }
6252
+ }
6253
+ pCur->iValue = pCur->iBase;
6254
+ pCur->bDone = 0;
66236255
return SQLITE_OK;
6256
+
6257
+series_no_rows:
6258
+ pCur->iBase = 0;
6259
+ pCur->iTerm = 0;
6260
+ pCur->iStep = 1;
6261
+ pCur->bDesc = 0;
6262
+ pCur->bDone = 1;
6263
+ return SQLITE_OK;
66246264
}
66256265
66266266
/*
66276267
** SQLite will invoke this method one or more times while planning a query
66286268
** that uses the generate_series virtual table. This routine needs to create
@@ -6948,10 +6588,12 @@
69486588
** expression and M is the size of the input string. The matcher never
69496589
** exhibits exponential behavior. Note that the X{p,q} operator expands
69506590
** to p copies of X following by q-p copies of X? and that the size of the
69516591
** regular expression in the O(N*M) performance bound is computed after
69526592
** this expansion.
6593
+**
6594
+** To help prevent DoS attacks, the maximum size of the NFA is restricted.
69536595
*/
69546596
#include <string.h>
69556597
#include <stdlib.h>
69566598
/* #include "sqlite3ext.h" */
69576599
SQLITE_EXTENSION_INIT1
@@ -6988,36 +6630,10 @@
69886630
#define RE_OP_NOTDIGIT 14 /* Not a digit */
69896631
#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
69906632
#define RE_OP_NOTSPACE 16 /* Not a digit */
69916633
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
69926634
#define RE_OP_ATSTART 18 /* Currently at the start of the string */
6993
-
6994
-#if defined(SQLITE_DEBUG)
6995
-/* Opcode names used for symbolic debugging */
6996
-static const char *ReOpName[] = {
6997
- "EOF",
6998
- "MATCH",
6999
- "ANY",
7000
- "ANYSTAR",
7001
- "FORK",
7002
- "GOTO",
7003
- "ACCEPT",
7004
- "CC_INC",
7005
- "CC_EXC",
7006
- "CC_VALUE",
7007
- "CC_RANGE",
7008
- "WORD",
7009
- "NOTWORD",
7010
- "DIGIT",
7011
- "NOTDIGIT",
7012
- "SPACE",
7013
- "NOTSPACE",
7014
- "BOUNDARY",
7015
- "ATSTART",
7016
-};
7017
-#endif /* SQLITE_DEBUG */
7018
-
70196635
70206636
/* Each opcode is a "state" in the NFA */
70216637
typedef unsigned short ReStateNumber;
70226638
70236639
/* Because this is an NFA and not a DFA, multiple states can be active at
@@ -7051,10 +6667,11 @@
70516667
unsigned (*xNextChar)(ReInput*); /* Next character function */
70526668
unsigned char zInit[12]; /* Initial text to match */
70536669
int nInit; /* Number of bytes in zInit */
70546670
unsigned nState; /* Number of entries in aOp[] and aArg[] */
70556671
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
6672
+ unsigned mxAlloc; /* Complexity limit */
70566673
};
70576674
70586675
/* Add a state to the given state set if it is not already there */
70596676
static void re_add_state(ReStateSet *pSet, int newState){
70606677
unsigned i;
@@ -7265,18 +6882,19 @@
72656882
return rc;
72666883
}
72676884
72686885
/* Resize the opcode and argument arrays for an RE under construction.
72696886
*/
7270
-static int re_resize(ReCompiled *p, int N){
6887
+static int re_resize(ReCompiled *p, unsigned int N){
72716888
char *aOp;
72726889
int *aArg;
6890
+ if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
72736891
aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
7274
- if( aOp==0 ) return 1;
6892
+ if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
72756893
p->aOp = aOp;
72766894
aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
7277
- if( aArg==0 ) return 1;
6895
+ if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
72786896
p->aArg = aArg;
72796897
p->nAlloc = N;
72806898
return 0;
72816899
}
72826900
@@ -7303,11 +6921,11 @@
73036921
}
73046922
73056923
/* Make a copy of N opcodes starting at iStart onto the end of the RE
73066924
** under construction.
73076925
*/
7308
-static void re_copy(ReCompiled *p, int iStart, int N){
6926
+static void re_copy(ReCompiled *p, int iStart, unsigned int N){
73096927
if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
73106928
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
73116929
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
73126930
p->nState += N;
73136931
}
@@ -7456,22 +7074,30 @@
74567074
case '^': {
74577075
re_append(p, RE_OP_ATSTART, 0);
74587076
break;
74597077
}
74607078
case '{': {
7461
- int m = 0, n = 0;
7462
- int sz, j;
7079
+ unsigned int m = 0, n = 0;
7080
+ unsigned int sz, j;
74637081
if( iPrev<0 ) return "'{m,n}' without operand";
7464
- while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
7082
+ while( (c=rePeek(p))>='0' && c<='9' ){
7083
+ m = m*10 + c - '0';
7084
+ if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
7085
+ p->sIn.i++;
7086
+ }
74657087
n = m;
74667088
if( c==',' ){
74677089
p->sIn.i++;
74687090
n = 0;
7469
- while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
7091
+ while( (c=rePeek(p))>='0' && c<='9' ){
7092
+ n = n*10 + c-'0';
7093
+ if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
7094
+ p->sIn.i++;
7095
+ }
74707096
}
74717097
if( c!='}' ) return "unmatched '{'";
7472
- if( n>0 && n<m ) return "n less than m in '{m,n}'";
7098
+ if( n<m ) return "n less than m in '{m,n}'";
74737099
p->sIn.i++;
74747100
sz = p->nState - iPrev;
74757101
if( m==0 ){
74767102
if( n==0 ) return "both m and n are zero in '{m,n}'";
74777103
re_insert(p, iPrev, RE_OP_FORK, sz+1);
@@ -7483,11 +7109,11 @@
74837109
for(j=m; j<n; j++){
74847110
re_append(p, RE_OP_FORK, sz+1);
74857111
re_copy(p, iPrev, sz);
74867112
}
74877113
if( n==0 && m>0 ){
7488
- re_append(p, RE_OP_FORK, -sz);
7114
+ re_append(p, RE_OP_FORK, -(int)sz);
74897115
}
74907116
break;
74917117
}
74927118
case '[': {
74937119
unsigned int iFirst = p->nState;
@@ -7549,12 +7175,11 @@
75497175
75507176
/* Free and reclaim all the memory used by a previously compiled
75517177
** regular expression. Applications should invoke this routine once
75527178
** for every call to re_compile() to avoid memory leaks.
75537179
*/
7554
-static void re_free(void *p){
7555
- ReCompiled *pRe = (ReCompiled*)p;
7180
+static void re_free(ReCompiled *pRe){
75567181
if( pRe ){
75577182
sqlite3_free(pRe->aOp);
75587183
sqlite3_free(pRe->aArg);
75597184
sqlite3_free(pRe);
75607185
}
@@ -7564,11 +7189,16 @@
75647189
** Compile a textual regular expression in zIn[] into a compiled regular
75657190
** expression suitable for us by re_match() and return a pointer to the
75667191
** compiled regular expression in *ppRe. Return NULL on success or an
75677192
** error message if something goes wrong.
75687193
*/
7569
-static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
7194
+static const char *re_compile(
7195
+ ReCompiled **ppRe, /* OUT: write compiled NFA here */
7196
+ const char *zIn, /* Input regular expression */
7197
+ int mxRe, /* Complexity limit */
7198
+ int noCase /* True for caseless comparisons */
7199
+){
75707200
ReCompiled *pRe;
75717201
const char *zErr;
75727202
int i, j;
75737203
75747204
*ppRe = 0;
@@ -7576,13 +7206,15 @@
75767206
if( pRe==0 ){
75777207
return "out of memory";
75787208
}
75797209
memset(pRe, 0, sizeof(*pRe));
75807210
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
7211
+ pRe->mxAlloc = mxRe;
75817212
if( re_resize(pRe, 30) ){
7213
+ zErr = pRe->zErr;
75827214
re_free(pRe);
7583
- return "out of memory";
7215
+ return zErr;
75847216
}
75857217
if( zIn[0]=='^' ){
75867218
zIn++;
75877219
}else{
75887220
re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -7630,10 +7262,18 @@
76307262
if( j>0 && pRe->zInit[j-1]==0 ) j--;
76317263
pRe->nInit = j;
76327264
}
76337265
return pRe->zErr;
76347266
}
7267
+
7268
+/*
7269
+** Compute a reasonable limit on the length of the REGEXP NFA.
7270
+*/
7271
+static int re_maxlen(sqlite3_context *context){
7272
+ sqlite3 *db = sqlite3_context_db_handle(context);
7273
+ return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2;
7274
+}
76357275
76367276
/*
76377277
** Implementation of the regexp() SQL function. This function implements
76387278
** the build-in REGEXP operator. The first argument to the function is the
76397279
** pattern and the second argument is the string. So, the SQL statements:
@@ -7656,11 +7296,12 @@
76567296
(void)argc; /* Unused */
76577297
pRe = sqlite3_get_auxdata(context, 0);
76587298
if( pRe==0 ){
76597299
zPattern = (const char*)sqlite3_value_text(argv[0]);
76607300
if( zPattern==0 ) return;
7661
- zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
7301
+ zErr = re_compile(&pRe, zPattern, re_maxlen(context),
7302
+ sqlite3_user_data(context)!=0);
76627303
if( zErr ){
76637304
re_free(pRe);
76647305
sqlite3_result_error(context, zErr, -1);
76657306
return;
76667307
}
@@ -7698,14 +7339,36 @@
76987339
sqlite3_str *pStr;
76997340
int i;
77007341
int n;
77017342
char *z;
77027343
(void)argc;
7344
+ static const char *ReOpName[] = {
7345
+ "EOF",
7346
+ "MATCH",
7347
+ "ANY",
7348
+ "ANYSTAR",
7349
+ "FORK",
7350
+ "GOTO",
7351
+ "ACCEPT",
7352
+ "CC_INC",
7353
+ "CC_EXC",
7354
+ "CC_VALUE",
7355
+ "CC_RANGE",
7356
+ "WORD",
7357
+ "NOTWORD",
7358
+ "DIGIT",
7359
+ "NOTDIGIT",
7360
+ "SPACE",
7361
+ "NOTSPACE",
7362
+ "BOUNDARY",
7363
+ "ATSTART",
7364
+ };
77037365
77047366
zPattern = (const char*)sqlite3_value_text(argv[0]);
77057367
if( zPattern==0 ) return;
7706
- zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
7368
+ zErr = re_compile(&pRe, zPattern, re_maxlen(context),
7369
+ sqlite3_user_data(context)!=0);
77077370
if( zErr ){
77087371
re_free(pRe);
77097372
sqlite3_result_error(context, zErr, -1);
77107373
return;
77117374
}
@@ -9284,18 +8947,20 @@
92848947
if( idxNum & 1 ){
92858948
pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
92868949
if( pCur->nPrefix>0 ){
92878950
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
92888951
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
8952
+ pCur->nPrefix = (int)strlen(pCur->zPrefix);
92898953
}
92908954
iArg = 1;
92918955
}
92928956
if( idxNum & 2 ){
92938957
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
92948958
if( pCur->nLine>0 ){
92958959
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
92968960
if( pCur->zLine==0 ) return SQLITE_NOMEM;
8961
+ pCur->nLine = (int)strlen(pCur->zLine);
92978962
}
92988963
}
92998964
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
93008965
int i = pCur->nLine;
93018966
while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
@@ -9303,10 +8968,11 @@
93038968
}
93048969
pCur->nPrefix = pCur->nLine - i;
93058970
if( pCur->nPrefix>0 ){
93068971
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
93078972
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
8973
+ pCur->nPrefix = (int)strlen(pCur->zPrefix);
93088974
}
93098975
}
93108976
pCur->iRowid = 0;
93118977
pCur->ePhase = COMPLETION_FIRST_PHASE;
93128978
return completionNext(pVtabCursor);
@@ -10216,11 +9882,17 @@
102169882
"method," /* 6: Compression method (integer) */
102179883
"z HIDDEN" /* 7: Name of zip file */
102189884
") WITHOUT ROWID;";
102199885
102209886
#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
10221
-#define ZIPFILE_BUFFER_SIZE (64*1024)
9887
+#define ZIPFILE_MX_NAME (250) /* Windows limitation on filename size */
9888
+
9889
+/*
9890
+** The buffer should be large enough to contain 3 65536 byte strings - the
9891
+** filename, the extra field and the file comment.
9892
+*/
9893
+#define ZIPFILE_BUFFER_SIZE (200*1024)
102229894
102239895
102249896
/*
102259897
** Magic numbers used to read and write zip files.
102269898
**
@@ -10773,10 +10445,11 @@
1077310445
pLFH->crc32 = zipfileRead32(aRead);
1077410446
pLFH->szCompressed = zipfileRead32(aRead);
1077510447
pLFH->szUncompressed = zipfileRead32(aRead);
1077610448
pLFH->nFile = zipfileRead16(aRead);
1077710449
pLFH->nExtra = zipfileRead16(aRead);
10450
+ if( pLFH->nFile>ZIPFILE_MX_NAME ) rc = SQLITE_ERROR;
1077810451
}
1077910452
return rc;
1078010453
}
1078110454
1078210455
@@ -10898,10 +10571,19 @@
1089810571
|| mUnixTime==zipfileMtime(pCds)
1089910572
|| ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
1090010573
/* || (mUnixTime % 2) */
1090110574
);
1090210575
}
10576
+
10577
+/*
10578
+** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a
10579
+** generic corruption message and return SQLITE_CORRUPT;
10580
+*/
10581
+static int zipfileCorrupt(char **pzErr){
10582
+ *pzErr = sqlite3_mprintf("zip archive is corrupt");
10583
+ return SQLITE_CORRUPT;
10584
+}
1090310585
1090410586
/*
1090510587
** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
1090610588
** size) containing an entire zip archive image. Or, if aBlob is NULL,
1090710589
** then pFile is a file-handle open on a zip file. In either case, this
@@ -10921,16 +10603,19 @@
1092110603
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
1092210604
){
1092310605
u8 *aRead;
1092410606
char **pzErr = &pTab->base.zErrMsg;
1092510607
int rc = SQLITE_OK;
10926
- (void)nBlob;
1092710608
1092810609
if( aBlob==0 ){
1092910610
aRead = pTab->aBuffer;
1093010611
rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
1093110612
}else{
10613
+ if( (iOff+ZIPFILE_CDS_FIXED_SZ)>nBlob ){
10614
+ /* Not enough data for the CDS structure. Corruption. */
10615
+ return zipfileCorrupt(pzErr);
10616
+ }
1093210617
aRead = (u8*)&aBlob[iOff];
1093310618
}
1093410619
1093510620
if( rc==SQLITE_OK ){
1093610621
sqlite3_int64 nAlloc;
@@ -10957,10 +10642,13 @@
1095710642
rc = zipfileReadData(
1095810643
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
1095910644
);
1096010645
}else{
1096110646
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
10647
+ if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){
10648
+ rc = zipfileCorrupt(pzErr);
10649
+ }
1096210650
}
1096310651
}
1096410652
1096510653
if( rc==SQLITE_OK ){
1096610654
u32 *pt = &pNew->mUnixTime;
@@ -10979,19 +10667,26 @@
1097910667
ZipfileLFH lfh;
1098010668
if( pFile ){
1098110669
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
1098210670
}else{
1098310671
aRead = (u8*)&aBlob[pNew->cds.iOffset];
10672
+ if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
10673
+ rc = zipfileCorrupt(pzErr);
10674
+ }
1098410675
}
1098510676
1098610677
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
1098710678
if( rc==SQLITE_OK ){
1098810679
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
1098910680
pNew->iDataOff += lfh.nFile + lfh.nExtra;
1099010681
if( aBlob && pNew->cds.szCompressed ){
10991
- pNew->aData = &pNew->aExtra[nExtra];
10992
- memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
10682
+ if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
10683
+ rc = zipfileCorrupt(pzErr);
10684
+ }else{
10685
+ pNew->aData = &pNew->aExtra[nExtra];
10686
+ memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
10687
+ }
1099310688
}
1099410689
}else{
1099510690
*pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
1099610691
(int)pNew->cds.iOffset
1099710692
);
@@ -11774,10 +11469,15 @@
1177411469
1177511470
if( rc==SQLITE_OK ){
1177611471
zPath = (const char*)sqlite3_value_text(apVal[2]);
1177711472
if( zPath==0 ) zPath = "";
1177811473
nPath = (int)strlen(zPath);
11474
+ if( nPath>ZIPFILE_MX_NAME ){
11475
+ zipfileTableErr(pTab, "filename too long; max: %d bytes",
11476
+ ZIPFILE_MX_NAME);
11477
+ rc = SQLITE_CONSTRAINT;
11478
+ }
1177911479
mTime = zipfileGetTime(apVal[4]);
1178011480
}
1178111481
1178211482
if( rc==SQLITE_OK && bIsDir ){
1178311483
/* For a directory, check that the last character in the path is a
@@ -12135,10 +11835,17 @@
1213511835
if( zName==0 ){
1213611836
zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
1213711837
rc = SQLITE_ERROR;
1213811838
goto zipfile_step_out;
1213911839
}
11840
+ if( nName>ZIPFILE_MX_NAME ){
11841
+ zErr = sqlite3_mprintf(
11842
+ "filename argument to zipfile() too big; max: %d bytes",
11843
+ ZIPFILE_MX_NAME);
11844
+ rc = SQLITE_ERROR;
11845
+ goto zipfile_step_out;
11846
+ }
1214011847
1214111848
/* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
1214211849
** deflate compression) or NULL (choose automatically). */
1214311850
if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
1214411851
iMethod = (int)sqlite3_value_int64(pMethod);
@@ -12477,10 +12184,11 @@
1247712184
return rc;
1247812185
}
1247912186
1248012187
/************************* End ../ext/misc/sqlar.c ********************/
1248112188
#endif
12189
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
1248212190
/************************* Begin ../ext/expert/sqlite3expert.h ******************/
1248312191
/*
1248412192
** 2017 April 07
1248512193
**
1248612194
** The author disclaims copyright to this source code. In place of
@@ -14885,10 +14593,11 @@
1488514593
}
1488614594
1488714595
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
1488814596
1488914597
/************************* End ../ext/expert/sqlite3expert.c ********************/
14598
+#endif
1489014599
/************************* Begin ../ext/intck/sqlite3intck.h ******************/
1489114600
/*
1489214601
** 2024-02-08
1489314602
**
1489414603
** The author disclaims copyright to this source code. In place of
@@ -21525,15 +21234,17 @@
2152521234
char **azFilter; /* Array of xFilter rejection GLOB patterns */
2152621235
sqlite3_session *p; /* The open session */
2152721236
};
2152821237
#endif
2152921238
21239
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2153021240
typedef struct ExpertInfo ExpertInfo;
2153121241
struct ExpertInfo {
2153221242
sqlite3expert *pExpert;
2153321243
int bVerbose;
2153421244
};
21245
+#endif
2153521246
2153621247
/* A single line in the EQP output */
2153721248
typedef struct EQPGraphRow EQPGraphRow;
2153821249
struct EQPGraphRow {
2153921250
int iEqpId; /* ID for this row */
@@ -21633,11 +21344,13 @@
2163321344
int *aiIndent; /* Array of indents used in MODE_Explain */
2163421345
int nIndent; /* Size of array aiIndent[] */
2163521346
int iIndent; /* Index of current op in aiIndent[] */
2163621347
char *zNonce; /* Nonce for temporary safe-mode escapes */
2163721348
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
21349
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2163821350
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
21351
+#endif
2163921352
#ifdef SQLITE_SHELL_FIDDLE
2164021353
struct {
2164121354
const char * zInput; /* Input string from wasm/JS proxy */
2164221355
const char * zPos; /* Cursor pos into zInput */
2164321356
const char * zDefaultDbName; /* Default name for db file */
@@ -21661,13 +21374,12 @@
2166121374
*/
2166221375
#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
2166321376
#define SHELL_OPEN_NORMAL 1 /* Normal database file */
2166421377
#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
2166521378
#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
21666
-#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
21667
-#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
21668
-#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
21379
+#define SHELL_OPEN_DESERIALIZE 4 /* Open using sqlite3_deserialize() */
21380
+#define SHELL_OPEN_HEXDB 5 /* Use "dbtotxt" output as data source */
2166921381
2167021382
/* Allowed values for ShellState.eTraceType
2167121383
*/
2167221384
#define SHELL_TRACE_PLAIN 0 /* Show input SQL text */
2167321385
#define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */
@@ -22006,11 +21718,11 @@
2200621718
*/
2200721719
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
2200821720
int i;
2200921721
unsigned char *aBlob = (unsigned char*)pBlob;
2201021722
22011
- char *zStr = sqlite3_malloc(nBlob*2 + 1);
21723
+ char *zStr = sqlite3_malloc64((i64)nBlob*2 + 1);
2201221724
shell_check_oom(zStr);
2201321725
2201421726
for(i=0; i<nBlob; i++){
2201521727
static const char aHex[] = {
2201621728
'0', '1', '2', '3', '4', '5', '6', '7',
@@ -22027,11 +21739,11 @@
2202721739
2202821740
/*
2202921741
** Output the given string as a quoted string using SQL quoting conventions:
2203021742
**
2203121743
** (1) Single quotes (') within the string are doubled
22032
-** (2) The whle string is enclosed in '...'
21744
+** (2) The while string is enclosed in '...'
2203321745
** (3) Control characters other than \n, \t, and \r\n are escaped
2203421746
** using \u00XX notation and if such substitutions occur,
2203521747
** the whole string is enclosed in unistr('...') instead of '...'.
2203621748
**
2203721749
** Step (3) is omitted if the control-character escape mode is OFF.
@@ -22273,11 +21985,11 @@
2227321985
**
2227421986
** Escaping is needed if the string contains any control characters
2227521987
** other than \t, \n, and \r\n
2227621988
**
2227721989
** If no escaping is needed (the common case) then set *ppFree to NULL
22278
-** and return the original string. If escapingn is needed, write the
21990
+** and return the original string. If escaping is needed, write the
2227921991
** escaped string into memory obtained from sqlite3_malloc64() or the
2228021992
** equivalent, and return the new string and set *ppFree to the new string
2228121993
** as well.
2228221994
**
2228321995
** The caller is responsible for freeing *ppFree if it is non-NULL in order
@@ -23278,32 +22990,21 @@
2327822990
** Set the destination table field of the ShellState structure to
2327922991
** the name of the table given. Escape any quote characters in the
2328022992
** table name.
2328122993
*/
2328222994
static void set_table_name(ShellState *p, const char *zName){
23283
- int i, n;
23284
- char cQuote;
23285
- char *z;
23286
-
2328722995
if( p->zDestTable ){
23288
- free(p->zDestTable);
22996
+ sqlite3_free(p->zDestTable);
2328922997
p->zDestTable = 0;
2329022998
}
2329122999
if( zName==0 ) return;
23292
- cQuote = quoteChar(zName);
23293
- n = strlen30(zName);
23294
- if( cQuote ) n += n+2;
23295
- z = p->zDestTable = malloc( n+1 );
23296
- shell_check_oom(z);
23297
- n = 0;
23298
- if( cQuote ) z[n++] = cQuote;
23299
- for(i=0; zName[i]; i++){
23300
- z[n++] = zName[i];
23301
- if( zName[i]==cQuote ) z[n++] = cQuote;
23302
- }
23303
- if( cQuote ) z[n++] = cQuote;
23304
- z[n] = 0;
23000
+ if( quoteChar(zName) ){
23001
+ p->zDestTable = sqlite3_mprintf("\"%w\"", zName);
23002
+ }else{
23003
+ p->zDestTable = sqlite3_mprintf("%s", zName);
23004
+ }
23005
+ shell_check_oom(p->zDestTable);
2330523006
}
2330623007
2330723008
/*
2330823009
** Maybe construct two lines of text that point out the position of a
2330923010
** syntax error. Return a pointer to the text, in memory obtained from
@@ -23496,12 +23197,12 @@
2349623197
static int display_stats(
2349723198
sqlite3 *db, /* Database to query */
2349823199
ShellState *pArg, /* Pointer to ShellState */
2349923200
int bReset /* True to reset the stats */
2350023201
){
23501
- int iCur;
23502
- int iHiwtr;
23202
+ int iCur, iHiwtr;
23203
+ sqlite3_int64 iCur64, iHiwtr64;
2350323204
FILE *out;
2350423205
if( pArg==0 || pArg->out==0 ) return 0;
2350523206
out = pArg->out;
2350623207
2350723208
if( pArg->pStmt && pArg->statsOn==2 ){
@@ -23586,18 +23287,25 @@
2358623287
"Page cache hits: %d\n", iCur);
2358723288
iHiwtr = iCur = -1;
2358823289
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
2358923290
sqlite3_fprintf(out,
2359023291
"Page cache misses: %d\n", iCur);
23292
+ iHiwtr64 = iCur64 = -1;
23293
+ sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
23294
+ 0);
2359123295
iHiwtr = iCur = -1;
2359223296
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
2359323297
sqlite3_fprintf(out,
2359423298
"Page cache writes: %d\n", iCur);
2359523299
iHiwtr = iCur = -1;
2359623300
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
2359723301
sqlite3_fprintf(out,
2359823302
"Page cache spills: %d\n", iCur);
23303
+ sqlite3_fprintf(out,
23304
+ "Temporary data spilled to disk: %lld\n", iCur64);
23305
+ sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
23306
+ 1);
2359923307
iHiwtr = iCur = -1;
2360023308
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
2360123309
sqlite3_fprintf(out,
2360223310
"Schema Heap Usage: %d bytes\n", iCur);
2360323311
iHiwtr = iCur = -1;
@@ -23999,10 +23707,40 @@
2399923707
char *zBuf = sqlite3_malloc64( szVar-5 );
2400023708
if( zBuf ){
2400123709
memcpy(zBuf, &zVar[6], szVar-5);
2400223710
sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
2400323711
}
23712
+#ifdef SQLITE_ENABLE_CARRAY
23713
+ }else if( strncmp(zVar, "$carray_", 8)==0 ){
23714
+ static char *azColorNames[] = {
23715
+ "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold",
23716
+ "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon",
23717
+ "navy", "olive", "orange", "pink", "purple", "red", "silver",
23718
+ "tan", "teal", "violet", "white", "yellow"
23719
+ };
23720
+ static int aPrimes[] = {
23721
+ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
23722
+ 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
23723
+ };
23724
+ /* Special bindings: carray($carray_clr), carray($carray_primes)
23725
+ ** with --unsafe-testing: carray($carray_clr_p,26,'char*'),
23726
+ ** carray($carray_primes_p,26,'int32')
23727
+ */
23728
+ if( strcmp(zVar+8,"clr")==0 ){
23729
+ sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0);
23730
+ }else if( strcmp(zVar+8,"primes")==0 ){
23731
+ sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0);
23732
+ }else if( strcmp(zVar+8,"clr_p")==0
23733
+ && ShellHasFlag(pArg,SHFLG_TestingMode) ){
23734
+ sqlite3_bind_pointer(pStmt,i,azColorNames,"carray",0);
23735
+ }else if( strcmp(zVar+8,"primes_p")==0
23736
+ && ShellHasFlag(pArg,SHFLG_TestingMode) ){
23737
+ sqlite3_bind_pointer(pStmt,i,aPrimes,"carray",0);
23738
+ }else{
23739
+ sqlite3_bind_null(pStmt, i);
23740
+ }
23741
+#endif
2400423742
}else{
2400523743
sqlite3_bind_null(pStmt, i);
2400623744
}
2400723745
sqlite3_reset(pQ);
2400823746
}
@@ -24581,11 +24319,11 @@
2458124319
}
2458224320
}
2458324321
}
2458424322
}
2458524323
24586
-#ifndef SQLITE_OMIT_VIRTUALTABLE
24324
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2458724325
/*
2458824326
** This function is called to process SQL if the previous shell command
2458924327
** was ".expert". It passes the SQL in the second argument directly to
2459024328
** the sqlite3expert object.
2459124329
**
@@ -24713,11 +24451,11 @@
2471324451
}
2471424452
sqlite3_free(zErr);
2471524453
2471624454
return rc;
2471724455
}
24718
-#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
24456
+#endif /* !SQLITE_OMIT_VIRTUALTABLE && !SQLITE_OMIT_AUTHORIZATION */
2471924457
2472024458
/*
2472124459
** Execute a statement or set of statements. Print
2472224460
** any result rows/columns depending on the current mode
2472324461
** set via the supplied callback.
@@ -24739,11 +24477,11 @@
2473924477
2474024478
if( pzErrMsg ){
2474124479
*pzErrMsg = NULL;
2474224480
}
2474324481
24744
-#ifndef SQLITE_OMIT_VIRTUALTABLE
24482
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2474524483
if( pArg->expert.pExpert ){
2474624484
rc = expertHandleSQL(pArg, zSql, pzErrMsg);
2474724485
return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
2474824486
}
2474924487
#endif
@@ -24901,11 +24639,11 @@
2490124639
static char **tableColumnList(ShellState *p, const char *zTab){
2490224640
char **azCol = 0;
2490324641
sqlite3_stmt *pStmt;
2490424642
char *zSql;
2490524643
int nCol = 0;
24906
- int nAlloc = 0;
24644
+ i64 nAlloc = 0;
2490724645
int nPK = 0; /* Number of PRIMARY KEY columns seen */
2490824646
int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
2490924647
int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid);
2491024648
int rc;
2491124649
@@ -24915,11 +24653,11 @@
2491524653
sqlite3_free(zSql);
2491624654
if( rc ) return 0;
2491724655
while( sqlite3_step(pStmt)==SQLITE_ROW ){
2491824656
if( nCol>=nAlloc-2 ){
2491924657
nAlloc = nAlloc*2 + nCol + 10;
24920
- azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
24658
+ azCol = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
2492124659
shell_check_oom(azCol);
2492224660
}
2492324661
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
2492424662
shell_check_oom(azCol[nCol]);
2492524663
if( sqlite3_column_int(pStmt, 5) ){
@@ -25106,17 +24844,17 @@
2510624844
appendText(&sSelect, " FROM ", 0);
2510724845
appendText(&sSelect, zTable, quoteChar(zTable));
2510824846
2510924847
savedDestTable = p->zDestTable;
2511024848
savedMode = p->mode;
25111
- p->zDestTable = sTable.z;
24849
+ p->zDestTable = sTable.zTxt;
2511224850
p->mode = p->cMode = MODE_Insert;
25113
- rc = shell_exec(p, sSelect.z, 0);
24851
+ rc = shell_exec(p, sSelect.zTxt, 0);
2511424852
if( (rc&0xff)==SQLITE_CORRUPT ){
2511524853
sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
2511624854
toggleSelectOrder(p->db);
25117
- shell_exec(p, sSelect.z, 0);
24855
+ shell_exec(p, sSelect.zTxt, 0);
2511824856
toggleSelectOrder(p->db);
2511924857
}
2512024858
p->zDestTable = savedDestTable;
2512124859
p->mode = savedMode;
2512224860
freeText(&sTable);
@@ -25245,11 +24983,13 @@
2524524983
" --bom Put a UTF8 byte-order mark on intermediate file",
2524624984
#endif
2524724985
#ifndef SQLITE_SHELL_FIDDLE
2524824986
".exit ?CODE? Exit this program with return-code CODE",
2524924987
#endif
24988
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2525024989
".expert EXPERIMENTAL. Suggest indexes for queries",
24990
+#endif
2525124991
".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
2525224992
".filectrl CMD ... Run various sqlite3_file_control() operations",
2525324993
" --schema SCHEMA Use SCHEMA instead of \"main\"",
2525424994
" --help Show CMD details",
2525524995
".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
@@ -25270,11 +25010,11 @@
2527025010
" from the \".mode\" output mode",
2527125011
" * If FILE begins with \"|\" then it is a command that generates the",
2527225012
" input text.",
2527325013
#endif
2527425014
#ifndef SQLITE_OMIT_TEST_CONTROL
25275
- ",imposter INDEX TABLE Create imposter table TABLE on index INDEX",
25015
+ ".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
2527625016
#endif
2527725017
".indexes ?TABLE? Show names of indexes",
2527825018
" If TABLE is specified, only show indexes for",
2527925019
" tables matching TABLE using the LIKE operator.",
2528025020
".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db",
@@ -25337,11 +25077,17 @@
2533725077
" Options:",
2533825078
" --append Use appendvfs to append database to the end of FILE",
2533925079
#endif
2534025080
#ifndef SQLITE_OMIT_DESERIALIZE
2534125081
" --deserialize Load into memory using sqlite3_deserialize()",
25082
+#endif
25083
+/*" --exclusive Set the SQLITE_OPEN_EXCLUSIVE flag", UNDOCUMENTED */
25084
+#ifndef SQLITE_OMIT_DESERIALIZE
2534225085
" --hexdb Load the output of \"dbtotxt\" as an in-memory db",
25086
+#endif
25087
+ " --ifexist Only open if FILE already exists",
25088
+#ifndef SQLITE_OMIT_DESERIALIZE
2534325089
" --maxsize N Maximum size for --hexdb or --deserialized database",
2534425090
#endif
2534525091
" --new Initialize FILE to an empty database",
2534625092
" --nofollow Do not follow symbolic links",
2534725093
" --readonly Open FILE readonly",
@@ -25726,14 +25472,14 @@
2572625472
** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
2572725473
*/
2572825474
static unsigned char *readHexDb(ShellState *p, int *pnData){
2572925475
unsigned char *a = 0;
2573025476
int nLine;
25731
- int n = 0;
25477
+ int n = 0; /* Size of db per first line of hex dump */
25478
+ i64 sz = 0; /* n rounded up to nearest page boundary */
2573225479
int pgsz = 0;
25733
- int iOffset = 0;
25734
- int j, k;
25480
+ i64 iOffset = 0;
2573525481
int rc;
2573625482
FILE *in;
2573725483
const char *zDbFilename = p->pAuxDb->zDbFilename;
2573825484
unsigned int x[16];
2573925485
char zLine[1000];
@@ -25753,20 +25499,21 @@
2575325499
nLine++;
2575425500
if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
2575525501
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
2575625502
if( rc!=2 ) goto readHexDb_error;
2575725503
if( n<0 ) goto readHexDb_error;
25758
- if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error;
25759
- n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
25760
- a = sqlite3_malloc( n ? n : 1 );
25761
- shell_check_oom(a);
25762
- memset(a, 0, n);
2576325504
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
2576425505
sqlite3_fputs("invalid pagesize\n", stderr);
2576525506
goto readHexDb_error;
2576625507
}
25508
+ sz = ((i64)n+pgsz-1)&~(pgsz-1); /* Round up to nearest multiple of pgsz */
25509
+ a = sqlite3_malloc64( sz ? sz : 1 );
25510
+ shell_check_oom(a);
25511
+ memset(a, 0, sz);
2576725512
for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
25513
+ int j = 0; /* Page number from "| page" line */
25514
+ int k = 0; /* Offset from "| page" line */
2576825515
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
2576925516
if( rc==2 ){
2577025517
iOffset = k;
2577125518
continue;
2577225519
}
@@ -25775,18 +25522,18 @@
2577525522
}
2577625523
rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
2577725524
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
2577825525
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
2577925526
if( rc==17 ){
25780
- k = iOffset+j;
25781
- if( k+16<=n && k>=0 ){
25527
+ i64 iOff = iOffset+j;
25528
+ if( iOff+16<=sz && iOff>=0 ){
2578225529
int ii;
25783
- for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
25530
+ for(ii=0; ii<16; ii++) a[iOff+ii] = x[ii]&0xff;
2578425531
}
2578525532
}
2578625533
}
25787
- *pnData = n;
25534
+ *pnData = sz;
2578825535
if( in!=p->in ){
2578925536
fclose(in);
2579025537
}else{
2579125538
p->lineno = nLine;
2579225539
}
@@ -25849,11 +25596,11 @@
2584925596
p->pLog = pSavedLog;
2585025597
2585125598
if( zFake ){
2585225599
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
2585325600
-1, sqlite3_free);
25854
- free(zFake);
25601
+ sqlite3_free(zFake);
2585525602
}
2585625603
}
2585725604
2585825605
/* Flags for open_db().
2585925606
**
@@ -25881,14 +25628,17 @@
2588125628
}else{
2588225629
p->openMode = (u8)deduceDatabaseType(zDbFilename,
2588325630
(openFlags & OPEN_DB_ZIPFILE)!=0);
2588425631
}
2588525632
}
25633
+ if( (p->openFlags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE))==0 ){
25634
+ if( p->openFlags==0 ) p->openFlags = SQLITE_OPEN_CREATE;
25635
+ p->openFlags |= SQLITE_OPEN_READWRITE;
25636
+ }
2588625637
switch( p->openMode ){
2588725638
case SHELL_OPEN_APPENDVFS: {
25888
- sqlite3_open_v2(zDbFilename, &p->db,
25889
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
25639
+ sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, "apndvfs");
2589025640
break;
2589125641
}
2589225642
case SHELL_OPEN_HEXDB:
2589325643
case SHELL_OPEN_DESERIALIZE: {
2589425644
sqlite3_open(0, &p->db);
@@ -25896,19 +25646,13 @@
2589625646
}
2589725647
case SHELL_OPEN_ZIPFILE: {
2589825648
sqlite3_open(":memory:", &p->db);
2589925649
break;
2590025650
}
25901
- case SHELL_OPEN_READONLY: {
25902
- sqlite3_open_v2(zDbFilename, &p->db,
25903
- SQLITE_OPEN_READONLY|p->openFlags, 0);
25904
- break;
25905
- }
2590625651
case SHELL_OPEN_UNSPEC:
2590725652
case SHELL_OPEN_NORMAL: {
25908
- sqlite3_open_v2(zDbFilename, &p->db,
25909
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
25653
+ sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, 0);
2591025654
break;
2591125655
}
2591225656
}
2591325657
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
2591425658
sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
@@ -25944,11 +25688,10 @@
2594425688
sqlite3_sha_init(p->db, 0, 0);
2594525689
sqlite3_shathree_init(p->db, 0, 0);
2594625690
sqlite3_uint_init(p->db, 0, 0);
2594725691
sqlite3_stmtrand_init(p->db, 0, 0);
2594825692
sqlite3_decimal_init(p->db, 0, 0);
25949
- sqlite3_percentile_init(p->db, 0, 0);
2595025693
sqlite3_base64_init(p->db, 0, 0);
2595125694
sqlite3_base85_init(p->db, 0, 0);
2595225695
sqlite3_regexp_init(p->db, 0, 0);
2595325696
sqlite3_ieee_init(p->db, 0, 0);
2595425697
sqlite3_series_init(p->db, 0, 0);
@@ -26043,13 +25786,15 @@
2604325786
}
2604425787
}
2604525788
#endif
2604625789
}
2604725790
if( p->db!=0 ){
25791
+#ifndef SQLITE_OMIT_AUTHORIZATION
2604825792
if( p->bSafeModePersist ){
2604925793
sqlite3_set_authorizer(p->db, safeModeAuth, p);
2605025794
}
25795
+#endif
2605125796
sqlite3_db_config(
2605225797
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
2605325798
);
2605425799
}
2605525800
}
@@ -26358,12 +26103,12 @@
2635826103
struct ImportCtx {
2635926104
const char *zFile; /* Name of the input file */
2636026105
FILE *in; /* Read the CSV text from this input stream */
2636126106
int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
2636226107
char *z; /* Accumulated text for a field */
26363
- int n; /* Number of bytes in z */
26364
- int nAlloc; /* Space allocated for z[] */
26108
+ i64 n; /* Number of bytes in z */
26109
+ i64 nAlloc; /* Space allocated for z[] */
2636526110
int nLine; /* Current line number */
2636626111
int nRow; /* Number of rows imported */
2636726112
int nErr; /* Number of errors encountered */
2636826113
int bNotFirst; /* True if one or more bytes already read */
2636926114
int cTerm; /* Character that terminated the most recent field */
@@ -26829,14 +26574,17 @@
2682926574
#if SQLITE_SHELL_HAVE_RECOVER
2683026575
/*
2683126576
** Convert a 2-byte or 4-byte big-endian integer into a native integer
2683226577
*/
2683326578
static unsigned int get2byteInt(unsigned char *a){
26834
- return (a[0]<<8) + a[1];
26579
+ return ((unsigned int)a[0]<<8) + (unsigned int)a[1];
2683526580
}
2683626581
static unsigned int get4byteInt(unsigned char *a){
26837
- return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
26582
+ return ((unsigned int)a[0]<<24)
26583
+ + ((unsigned int)a[1]<<16)
26584
+ + ((unsigned int)a[2]<<8)
26585
+ + (unsigned int)a[3];
2683826586
}
2683926587
2684026588
/*
2684126589
** Implementation of the ".dbinfo" command.
2684226590
**
@@ -26969,11 +26717,11 @@
2696926717
if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
2697026718
nPage = sqlite3_column_int64(pStmt, 0);
2697126719
sqlite3_finalize(pStmt);
2697226720
pStmt = 0;
2697326721
if( nPage<1 ) goto dbtotxt_error;
26974
- rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
26722
+ rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
2697526723
if( rc ) goto dbtotxt_error;
2697626724
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
2697726725
zTail = "unk.db";
2697826726
}else{
2697926727
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
@@ -26980,10 +26728,15 @@
2698026728
if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
2698126729
zTail = strrchr(zFilename, '/');
2698226730
#if defined(_WIN32)
2698326731
if( zTail==0 ) zTail = strrchr(zFilename, '\\');
2698426732
#endif
26733
+ if( zTail==0 ){
26734
+ zTail = zFilename;
26735
+ }else if( zTail[1]!=0 ){
26736
+ zTail++;
26737
+ }
2698526738
}
2698626739
zName = strdup(zTail);
2698726740
shell_check_oom(zName);
2698826741
sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
2698926742
nPage*pgSz, pgSz, zName);
@@ -27148,10 +26901,47 @@
2714826901
if( zStr[0]!='-' ) return 0;
2714926902
zStr++;
2715026903
if( zStr[0]=='-' ) zStr++;
2715126904
return cli_strcmp(zStr, zOpt)==0;
2715226905
}
26906
+
26907
+/*
26908
+** The input zFN is guaranteed to start with "file:" and is thus a URI
26909
+** filename. Extract the actual filename and return a pointer to that
26910
+** filename in spaced obtained from sqlite3_malloc().
26911
+**
26912
+** The caller is responsible for freeing space using sqlite3_free() when
26913
+** it has finished with the filename.
26914
+*/
26915
+static char *shellFilenameFromUri(const char *zFN){
26916
+ char *zOut;
26917
+ int i, j, d1, d2;
26918
+
26919
+ assert( cli_strncmp(zFN,"file:",5)==0 );
26920
+ zOut = sqlite3_mprintf("%s", zFN+5);
26921
+ shell_check_oom(zOut);
26922
+ for(i=j=0; zOut[i]!=0 && zOut[i]!='?'; i++){
26923
+ if( zOut[i]!='%' ){
26924
+ zOut[j++] = zOut[i];
26925
+ continue;
26926
+ }
26927
+ d1 = hexDigitValue(zOut[i+1]);
26928
+ if( d1<0 ){
26929
+ zOut[j] = 0;
26930
+ break;
26931
+ }
26932
+ d2 = hexDigitValue(zOut[i+2]);
26933
+ if( d2<0 ){
26934
+ zOut[j] = 0;
26935
+ break;
26936
+ }
26937
+ zOut[j++] = d1*16 + d2;
26938
+ i += 2;
26939
+ }
26940
+ zOut[j] = 0;
26941
+ return zOut;
26942
+}
2715326943
2715426944
/*
2715526945
** Delete a file.
2715626946
*/
2715726947
int shellDeleteFile(const char *zFilename){
@@ -28704,11 +28494,11 @@
2870428494
int nArg = 0;
2870528495
int n, c;
2870628496
int rc = 0;
2870728497
char *azArg[52];
2870828498
28709
-#ifndef SQLITE_OMIT_VIRTUALTABLE
28499
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2871028500
if( p->expert.pExpert ){
2871128501
expertFinish(p, 1, 0);
2871228502
}
2871328503
#endif
2871428504
@@ -29005,11 +28795,11 @@
2900528795
}else{
2900628796
while( sqlite3_step(pStmt)==SQLITE_ROW ){
2900728797
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
2900828798
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
2900928799
if( zSchema==0 || zFile==0 ) continue;
29010
- azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*));
28800
+ azName = sqlite3_realloc64(azName, (nName+1)*2*sizeof(char*));
2901128801
shell_check_oom(azName);
2901228802
azName[nName*2] = strdup(zSchema);
2901328803
azName[nName*2+1] = strdup(zFile);
2901428804
nName++;
2901528805
}
@@ -29208,10 +28998,11 @@
2920828998
rc = 1;
2920928999
}
2921029000
}else
2921129001
2921229002
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
29003
+ open_db(p, 0);
2921329004
rc = shell_dbtotxt_command(p, nArg, azArg);
2921429005
}else
2921529006
2921629007
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
2921729008
if( nArg==2 ){
@@ -29273,11 +29064,11 @@
2927329064
if( p->mode==MODE_Explain ) p->mode = p->normalMode;
2927429065
p->autoExplain = 1;
2927529066
}
2927629067
}else
2927729068
29278
-#ifndef SQLITE_OMIT_VIRTUALTABLE
29069
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
2927929070
if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
2928029071
if( p->bSafeMode ){
2928129072
sqlite3_fprintf(stderr,
2928229073
"Cannot run experimental commands such as \"%s\" in safe mode\n",
2928329074
azArg[0]);
@@ -29840,16 +29631,10 @@
2984029631
sqlite3_stmt *pStmt;
2984129632
int tnum = 0;
2984229633
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
2984329634
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
2984429635
int i;
29845
- if( !ShellHasFlag(p,SHFLG_TestingMode) ){
29846
- sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n",
29847
- "imposter");
29848
- rc = 1;
29849
- goto meta_command_exit;
29850
- }
2985129636
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
2985229637
eputz("Usage: .imposter INDEX IMPOSTER\n"
2985329638
" .imposter off\n");
2985429639
/* Also allowed, but not documented:
2985529640
**
@@ -29917,22 +29702,19 @@
2991729702
if( lenPK==0 ) lenPK = 100000;
2991829703
zSql = sqlite3_mprintf(
2991929704
"CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
2992029705
azArg[2], zCollist, lenPK, zCollist);
2992129706
sqlite3_free(zCollist);
29922
- rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
29707
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 2, tnum);
2992329708
if( rc==SQLITE_OK ){
2992429709
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
2992529710
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
2992629711
if( rc ){
2992729712
sqlite3_fprintf(stderr,
2992829713
"Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
2992929714
}else{
2993029715
sqlite3_fprintf(stdout, "%s;\n", zSql);
29931
- sqlite3_fprintf(stdout,
29932
- "WARNING: writing to an imposter table will corrupt"
29933
- " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index");
2993429716
}
2993529717
}else{
2993629718
sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
2993729719
rc = 1;
2993829720
}
@@ -30282,10 +30064,11 @@
3028230064
const char *zFN = 0; /* Pointer to constant filename */
3028330065
char *zNewFilename = 0; /* Name of the database file to open */
3028430066
int iName = 1; /* Index in azArg[] of the filename */
3028530067
int newFlag = 0; /* True to delete file before opening */
3028630068
int openMode = SHELL_OPEN_UNSPEC;
30069
+ int openFlags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
3028730070
3028830071
/* Check for command-line arguments */
3028930072
for(iName=1; iName<nArg; iName++){
3029030073
const char *z = azArg[iName];
3029130074
#ifndef SQLITE_SHELL_FIDDLE
@@ -30296,13 +30079,18 @@
3029630079
openMode = SHELL_OPEN_ZIPFILE;
3029730080
#endif
3029830081
}else if( optionMatch(z, "append") ){
3029930082
openMode = SHELL_OPEN_APPENDVFS;
3030030083
}else if( optionMatch(z, "readonly") ){
30301
- openMode = SHELL_OPEN_READONLY;
30084
+ openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
30085
+ openFlags |= SQLITE_OPEN_READONLY;
30086
+ }else if( optionMatch(z, "exclusive") ){
30087
+ openFlags |= SQLITE_OPEN_EXCLUSIVE;
30088
+ }else if( optionMatch(z, "ifexists") ){
30089
+ openFlags &= ~(SQLITE_OPEN_CREATE);
3030230090
}else if( optionMatch(z, "nofollow") ){
30303
- p->openFlags |= SQLITE_OPEN_NOFOLLOW;
30091
+ openFlags |= SQLITE_OPEN_NOFOLLOW;
3030430092
#ifndef SQLITE_OMIT_DESERIALIZE
3030530093
}else if( optionMatch(z, "deserialize") ){
3030630094
openMode = SHELL_OPEN_DESERIALIZE;
3030730095
}else if( optionMatch(z, "hexdb") ){
3030830096
openMode = SHELL_OPEN_HEXDB;
@@ -30330,16 +30118,25 @@
3033030118
p->db = 0;
3033130119
p->pAuxDb->zDbFilename = 0;
3033230120
sqlite3_free(p->pAuxDb->zFreeOnClose);
3033330121
p->pAuxDb->zFreeOnClose = 0;
3033430122
p->openMode = openMode;
30335
- p->openFlags = 0;
30123
+ p->openFlags = openFlags;
3033630124
p->szMax = 0;
3033730125
3033830126
/* If a filename is specified, try to open it first */
3033930127
if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
30340
- if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN);
30128
+ if( newFlag && zFN && !p->bSafeMode ){
30129
+ if( cli_strncmp(zFN,"file:",5)==0 ){
30130
+ char *zDel = shellFilenameFromUri(zFN);
30131
+ shell_check_oom(zDel);
30132
+ shellDeleteFile(zDel);
30133
+ sqlite3_free(zDel);
30134
+ }else{
30135
+ shellDeleteFile(zFN);
30136
+ }
30137
+ }
3034130138
#ifndef SQLITE_SHELL_FIDDLE
3034230139
if( p->bSafeMode
3034330140
&& p->openMode!=SHELL_OPEN_HEXDB
3034430141
&& zFN
3034530142
&& cli_strcmp(zFN,":memory:")!=0
@@ -30938,13 +30735,13 @@
3093830735
appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
3093930736
}
3094030737
appendText(&sSelect, "sql IS NOT NULL"
3094130738
" ORDER BY snum, rowid", 0);
3094230739
if( bDebug ){
30943
- sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z);
30740
+ sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.zTxt);
3094430741
}else{
30945
- rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
30742
+ rc = sqlite3_exec(p->db, sSelect.zTxt, callback, &data, &zErrMsg);
3094630743
}
3094730744
freeText(&sSelect);
3094830745
}
3094930746
if( zErrMsg ){
3095030747
shellEmitError(zErrMsg);
@@ -31072,19 +30869,20 @@
3107230869
3107330870
/* .session filter GLOB ....
3107430871
** Set a list of GLOB patterns of table names to be excluded.
3107530872
*/
3107630873
if( cli_strcmp(azCmd[0], "filter")==0 ){
31077
- int ii, nByte;
30874
+ int ii;
30875
+ i64 nByte;
3107830876
if( nCmd<2 ) goto session_syntax_error;
3107930877
if( pAuxDb->nSession ){
3108030878
for(ii=0; ii<pSession->nFilter; ii++){
3108130879
sqlite3_free(pSession->azFilter[ii]);
3108230880
}
3108330881
sqlite3_free(pSession->azFilter);
3108430882
nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
31085
- pSession->azFilter = sqlite3_malloc( nByte );
30883
+ pSession->azFilter = sqlite3_malloc64( nByte );
3108630884
shell_check_oom( pSession->azFilter );
3108730885
for(ii=1; ii<nCmd; ii++){
3108830886
char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
3108930887
shell_check_oom(x);
3109030888
}
@@ -31264,26 +31062,26 @@
3126431062
sqlite3_fprintf(p->out, "%s\n", zSql);
3126531063
}else
3126631064
if( cli_strcmp(zOp,"run")==0 ){
3126731065
char *zErrMsg = 0;
3126831066
str.n = 0;
31269
- str.z[0] = 0;
31067
+ str.zTxt[0] = 0;
3127031068
rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
3127131069
nTest++;
3127231070
if( bVerbose ){
31273
- sqlite3_fprintf(p->out, "Result: %s\n", str.z);
31071
+ sqlite3_fprintf(p->out, "Result: %s\n", str.zTxt);
3127431072
}
3127531073
if( rc || zErrMsg ){
3127631074
nErr++;
3127731075
rc = 1;
3127831076
sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
3127931077
sqlite3_free(zErrMsg);
31280
- }else if( cli_strcmp(zAns,str.z)!=0 ){
31078
+ }else if( cli_strcmp(zAns,str.zTxt)!=0 ){
3128131079
nErr++;
3128231080
rc = 1;
3128331081
sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns);
31284
- sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z);
31082
+ sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.zTxt);
3128531083
}
3128631084
}
3128731085
else{
3128831086
sqlite3_fprintf(stderr,
3128931087
"Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
@@ -31395,11 +31193,11 @@
3139531193
appendText(&sQuery, "SELECT * FROM ", 0);
3139631194
appendText(&sQuery, zTab, 0);
3139731195
appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
3139831196
}
3139931197
appendText(&sSql, zSep, 0);
31400
- appendText(&sSql, sQuery.z, '\'');
31198
+ appendText(&sSql, sQuery.zTxt, '\'');
3140131199
sQuery.n = 0;
3140231200
appendText(&sSql, ",", 0);
3140331201
appendText(&sSql, zTab, '\'');
3140431202
zSep = "),(";
3140531203
}
@@ -31407,17 +31205,17 @@
3140731205
if( bSeparate ){
3140831206
zSql = sqlite3_mprintf(
3140931207
"%s))"
3141031208
" SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label"
3141131209
" FROM [sha3sum$query]",
31412
- sSql.z, iSize);
31210
+ sSql.zTxt, iSize);
3141331211
}else{
3141431212
zSql = sqlite3_mprintf(
3141531213
"%s))"
3141631214
" SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash"
3141731215
" FROM [sha3sum$query]",
31418
- sSql.z, iSize);
31216
+ sSql.zTxt, iSize);
3141931217
}
3142031218
shell_check_oom(zSql);
3142131219
freeText(&sQuery);
3142231220
freeText(&sSql);
3142331221
if( bDebug ){
@@ -31613,11 +31411,11 @@
3161331411
goto meta_command_exit;
3161431412
}
3161531413
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
3161631414
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
3161731415
if( zDbName==0 ) continue;
31618
- if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0);
31416
+ if( s.zTxt && s.zTxt[0] ) appendText(&s, " UNION ALL ", 0);
3161931417
if( sqlite3_stricmp(zDbName, "main")==0 ){
3162031418
appendText(&s, "SELECT name FROM ", 0);
3162131419
}else{
3162231420
appendText(&s, "SELECT ", 0);
3162331421
appendText(&s, zDbName, '\'');
@@ -31635,11 +31433,11 @@
3163531433
}
3163631434
}
3163731435
rc = sqlite3_finalize(pStmt);
3163831436
if( rc==SQLITE_OK ){
3163931437
appendText(&s, " ORDER BY 1", 0);
31640
- rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
31438
+ rc = sqlite3_prepare_v2(p->db, s.zTxt, -1, &pStmt, 0);
3164131439
}
3164231440
freeText(&s);
3164331441
if( rc ) return shellDatabaseError(p->db);
3164431442
3164531443
/* Run the SQL statement prepared by the above block. Store the results
@@ -31652,14 +31450,14 @@
3165231450
sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
3165331451
}
3165431452
while( sqlite3_step(pStmt)==SQLITE_ROW ){
3165531453
if( nRow>=nAlloc ){
3165631454
char **azNew;
31657
- int n2 = nAlloc*2 + 10;
31455
+ sqlite3_int64 n2 = 2*(sqlite3_int64)nAlloc + 10;
3165831456
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
3165931457
shell_check_oom(azNew);
31660
- nAlloc = n2;
31458
+ nAlloc = (int)n2;
3166131459
azResult = azNew;
3166231460
}
3166331461
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
3166431462
shell_check_oom(azResult[nRow]);
3166531463
nRow++;
@@ -31818,11 +31616,11 @@
3181831616
{ 0x00000020, 1, "CoverIdxScan" },
3181931617
{ 0x00000040, 1, "OrderByIdxJoin" },
3182031618
{ 0x00000080, 1, "Transitive" },
3182131619
{ 0x00000100, 1, "OmitNoopJoin" },
3182231620
{ 0x00000200, 1, "CountOfView" },
31823
- { 0x00000400, 1, "CurosrHints" },
31621
+ { 0x00000400, 1, "CursorHints" },
3182431622
{ 0x00000800, 1, "Stat4" },
3182531623
{ 0x00001000, 1, "PushDown" },
3182631624
{ 0x00002000, 1, "SimplifyJoin" },
3182731625
{ 0x00004000, 1, "SkipScan" },
3182831626
{ 0x00008000, 1, "PropagateConst" },
@@ -32365,11 +32163,14 @@
3236532163
p->nWidth = nArg-1;
3236632164
p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
3236732165
if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
3236832166
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
3236932167
for(j=1; j<nArg; j++){
32370
- p->colWidth[j-1] = (int)integerValue(azArg[j]);
32168
+ i64 w = integerValue(azArg[j]);
32169
+ if( w < -30000 ) w = -30000;
32170
+ if( w > +30000 ) w = +30000;
32171
+ p->colWidth[j-1] = (int)w;
3237132172
}
3237232173
}else
3237332174
3237432175
{
3237532176
sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: "
@@ -32888,76 +32689,95 @@
3288832689
3288932690
return home_dir;
3289032691
}
3289132692
3289232693
/*
32893
-** On non-Windows platforms, look for $XDG_CONFIG_HOME.
32894
-** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
32895
-** the path to it. If there is no $(XDG_CONFIG_HOME) then
32896
-** look for $(HOME)/.config/sqlite3/sqliterc and if found
32897
-** return that. If none of these are found, return 0.
32694
+** On non-Windows platforms, look for:
32695
+**
32696
+** - ${zEnvVar}/${zBaseName}
32697
+** - ${HOME}/${zSubdir}/${zBaseName}
32698
+**
32699
+** $zEnvVar is intended to be the name of an XDG_... environment
32700
+** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
32701
+** NULL or getenv(zEnvVar) is NULL then fall back to the second
32702
+** option. If the selected option is not found in the filesystem,
32703
+** return 0.
32704
+**
32705
+** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
32706
+** becomes the fallback.
32707
+**
32708
+** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
32709
+** will conventionally be ".config" or ".local/state", which, not
32710
+** coincidentally, is the typical subdir of the corresponding XDG_...
32711
+** var with the XDG var's $HOME prefix.
3289832712
**
32899
-** The string returned is obtained from sqlite3_malloc() and
32900
-** should be freed by the caller.
32713
+** The returned string is obtained from sqlite3_malloc() and should be
32714
+** sqlite3_free()'d by the caller.
3290132715
*/
32902
-static char *find_xdg_config(void){
32716
+static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
32717
+ const char *zBaseName){
3290332718
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
3290432719
|| defined(__RTP__) || defined(_WRS_KERNEL)
3290532720
return 0;
3290632721
#else
32907
- char *zConfig = 0;
32908
- const char *zXdgHome;
32909
-
32910
- zXdgHome = getenv("XDG_CONFIG_HOME");
32911
- if( zXdgHome==0 ){
32912
- const char *zHome = getenv("HOME");
32913
- if( zHome==0 ) return 0;
32914
- zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
32722
+ char *zConfigFile = 0;
32723
+ const char *zXdgDir;
32724
+
32725
+ zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
32726
+ if( zXdgDir ){
32727
+ zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
3291532728
}else{
32916
- zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
32917
- }
32918
- shell_check_oom(zConfig);
32919
- if( access(zConfig,0)!=0 ){
32920
- sqlite3_free(zConfig);
32921
- zConfig = 0;
32922
- }
32923
- return zConfig;
32729
+ const char * zHome = find_home_dir(0);
32730
+ if( zHome==0 ) return 0;
32731
+ zConfigFile = (zSubdir && *zSubdir)
32732
+ ? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
32733
+ : sqlite3_mprintf("%s/%s", zHome, zBaseName);
32734
+ }
32735
+ shell_check_oom(zConfigFile);
32736
+ if( access(zConfigFile,0)!=0 ){
32737
+ sqlite3_free(zConfigFile);
32738
+ zConfigFile = 0;
32739
+ }
32740
+ return zConfigFile;
3292432741
#endif
3292532742
}
3292632743
3292732744
/*
32928
-** Read input from the file given by sqliterc_override. Or if that
32929
-** parameter is NULL, take input from the first of find_xdg_config()
32930
-** or ~/.sqliterc which is found.
32745
+** Read input from the file sqliterc_override. If that parameter is
32746
+** NULL, take it from find_xdg_file(), if found, or fall back to
32747
+** ~/.sqliterc.
3293132748
**
32932
-** Returns the number of errors.
32749
+** Failure to read the config is only considered a failure if
32750
+** sqliterc_override is not NULL, in which case this function may emit
32751
+** a warning or, if ::bail_on_error is true, fail fatally if the file
32752
+** named by sqliterc_override is not found.
3293332753
*/
3293432754
static void process_sqliterc(
3293532755
ShellState *p, /* Configuration data */
3293632756
const char *sqliterc_override /* Name of config file. NULL to use default */
3293732757
){
3293832758
char *home_dir = NULL;
32939
- const char *sqliterc = sqliterc_override;
32940
- char *zBuf = 0;
32759
+ char *sqliterc = (char*)sqliterc_override;
3294132760
FILE *inSaved = p->in;
3294232761
int savedLineno = p->lineno;
3294332762
3294432763
if( sqliterc == NULL ){
32945
- sqliterc = zBuf = find_xdg_config();
32764
+ sqliterc = find_xdg_file("XDG_CONFIG_HOME",
32765
+ ".config",
32766
+ "sqlite3/sqliterc");
3294632767
}
3294732768
if( sqliterc == NULL ){
3294832769
home_dir = find_home_dir(0);
3294932770
if( home_dir==0 ){
3295032771
eputz("-- warning: cannot find home directory;"
3295132772
" cannot read ~/.sqliterc\n");
3295232773
return;
3295332774
}
32954
- zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
32955
- shell_check_oom(zBuf);
32956
- sqliterc = zBuf;
32775
+ sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
32776
+ shell_check_oom(sqliterc);
3295732777
}
32958
- p->in = sqlite3_fopen(sqliterc,"rb");
32778
+ p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
3295932779
if( p->in ){
3296032780
if( stdin_is_interactive ){
3296132781
sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
3296232782
}
3296332783
if( process_input(p) && bail_on_error ) exit(1);
@@ -32966,11 +32786,13 @@
3296632786
sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
3296732787
if( bail_on_error ) exit(1);
3296832788
}
3296932789
p->in = inSaved;
3297032790
p->lineno = savedLineno;
32971
- sqlite3_free(zBuf);
32791
+ if( sqliterc != sqliterc_override ){
32792
+ sqlite3_free(sqliterc);
32793
+ }
3297232794
}
3297332795
3297432796
/*
3297532797
** Show available command line options
3297632798
*/
@@ -32997,10 +32819,11 @@
3299732819
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
3299832820
" -heap SIZE Size of heap for memsys3 or memsys5\n"
3299932821
#endif
3300032822
" -help show this message\n"
3300132823
" -html set output mode to HTML\n"
32824
+ " -ifexists only open if database already exists\n"
3300232825
" -interactive force interactive I/O\n"
3300332826
" -json set output mode to 'json'\n"
3300432827
" -line set output mode to 'line'\n"
3300532828
" -list set output mode to 'list'\n"
3300632829
" -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
@@ -33341,16 +33164,24 @@
3334133164
(void)cmdline_option_value(argc, argv, ++i);
3334233165
#endif
3334333166
}else if( cli_strcmp(z,"-pagecache")==0 ){
3334433167
sqlite3_int64 n, sz;
3334533168
sz = integerValue(cmdline_option_value(argc,argv,++i));
33346
- if( sz>70000 ) sz = 70000;
33169
+ if( sz>65536 ) sz = 65536;
3334733170
if( sz<0 ) sz = 0;
3334833171
n = integerValue(cmdline_option_value(argc,argv,++i));
3334933172
if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
3335033173
n = 0xffffffffffffLL/sz;
3335133174
}
33175
+ if( sz>0 && (sz & (sz-1))==0 ){
33176
+ /* If SIZE is a power of two, round it up by the PCACHE_HDRSZ */
33177
+ int szHdr = 0;
33178
+ sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &szHdr);
33179
+ sz += szHdr;
33180
+ sqlite3_fprintf(stdout, "Page cache size increased to %d to accommodate"
33181
+ " the %d-byte headers\n", (int)sz, szHdr);
33182
+ }
3335233183
verify_uninitialized();
3335333184
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
3335433185
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
3335533186
data.shellFlgs |= SHFLG_Pagecache;
3335633187
}else if( cli_strcmp(z,"-lookaside")==0 ){
@@ -33359,11 +33190,11 @@
3335933190
if( sz<0 ) sz = 0;
3336033191
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
3336133192
if( n<0 ) n = 0;
3336233193
verify_uninitialized();
3336333194
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
33364
- if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
33195
+ if( (i64)sz*(i64)n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
3336533196
}else if( cli_strcmp(z,"-threadsafe")==0 ){
3336633197
int n;
3336733198
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
3336833199
verify_uninitialized();
3336933200
switch( n ){
@@ -33401,13 +33232,19 @@
3340133232
data.openMode = SHELL_OPEN_DESERIALIZE;
3340233233
}else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
3340333234
data.szMax = integerValue(argv[++i]);
3340433235
#endif
3340533236
}else if( cli_strcmp(z,"-readonly")==0 ){
33406
- data.openMode = SHELL_OPEN_READONLY;
33237
+ data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
33238
+ data.openFlags |= SQLITE_OPEN_READONLY;
3340733239
}else if( cli_strcmp(z,"-nofollow")==0 ){
33408
- data.openFlags = SQLITE_OPEN_NOFOLLOW;
33240
+ data.openFlags |= SQLITE_OPEN_NOFOLLOW;
33241
+ }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
33242
+ data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
33243
+ }else if( cli_strcmp(z,"-ifexists")==0 ){
33244
+ data.openFlags &= ~(SQLITE_OPEN_CREATE);
33245
+ if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
3340933246
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
3341033247
}else if( cli_strncmp(z, "-A",2)==0 ){
3341133248
/* All remaining command-line arguments are passed to the ".archive"
3341233249
** command, so ignore them */
3341333250
break;
@@ -33557,13 +33394,19 @@
3355733394
data.openMode = SHELL_OPEN_DESERIALIZE;
3355833395
}else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
3355933396
data.szMax = integerValue(argv[++i]);
3356033397
#endif
3356133398
}else if( cli_strcmp(z,"-readonly")==0 ){
33562
- data.openMode = SHELL_OPEN_READONLY;
33399
+ data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
33400
+ data.openFlags |= SQLITE_OPEN_READONLY;
3356333401
}else if( cli_strcmp(z,"-nofollow")==0 ){
3356433402
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
33403
+ }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
33404
+ data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
33405
+ }else if( cli_strcmp(z,"-ifexists")==0 ){
33406
+ data.openFlags &= ~(SQLITE_OPEN_CREATE);
33407
+ if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
3356533408
}else if( cli_strcmp(z,"-ascii")==0 ){
3356633409
data.mode = MODE_Ascii;
3356733410
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit);
3356833411
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record);
3356933412
}else if( cli_strcmp(z,"-tabs")==0 ){
@@ -33734,11 +33577,10 @@
3373433577
/* Run commands received from standard input
3373533578
*/
3373633579
if( stdin_is_interactive ){
3373733580
char *zHome;
3373833581
char *zHistory;
33739
- int nHistory;
3374033582
sqlite3_fprintf(stdout,
3374133583
"SQLite version %s %.19s\n" /*extra-version-info*/
3374233584
"Enter \".help\" for usage hints.\n",
3374333585
sqlite3_libversion(), sqlite3_sourceid());
3374433586
if( warnInmemoryDb ){
@@ -33747,15 +33589,19 @@
3374733589
sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
3374833590
" persistent database.\n");
3374933591
}
3375033592
zHistory = getenv("SQLITE_HISTORY");
3375133593
if( zHistory ){
33752
- zHistory = strdup(zHistory);
33753
- }else if( (zHome = find_home_dir(0))!=0 ){
33754
- nHistory = strlen30(zHome) + 20;
33755
- if( (zHistory = malloc(nHistory))!=0 ){
33756
- sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
33594
+ zHistory = sqlite3_mprintf("%s", zHistory);
33595
+ shell_check_oom(zHistory);
33596
+ }else{
33597
+ zHistory = find_xdg_file("XDG_STATE_HOME",
33598
+ ".local/state",
33599
+ "sqlite_history");
33600
+ if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
33601
+ zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
33602
+ shell_check_oom(zHistory);
3375733603
}
3375833604
}
3375933605
if( zHistory ){ shell_read_history(zHistory); }
3376033606
#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
3376133607
rl_attempted_completion_function = readline_completion;
@@ -33767,21 +33613,21 @@
3376733613
data.in = 0;
3376833614
rc = process_input(&data);
3376933615
if( zHistory ){
3377033616
shell_stifle_history(2000);
3377133617
shell_write_history(zHistory);
33772
- free(zHistory);
33618
+ sqlite3_free(zHistory);
3377333619
}
3377433620
}else{
3377533621
data.in = stdin;
3377633622
rc = process_input(&data);
3377733623
}
3377833624
}
3377933625
#ifndef SQLITE_SHELL_FIDDLE
3378033626
/* In WASM mode we have to leave the db state in place so that
3378133627
** client code can "push" SQL into it after this call returns. */
33782
-#ifndef SQLITE_OMIT_VIRTUALTABLE
33628
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
3378333629
if( data.expert.pExpert ){
3378433630
expertFinish(&data, 1, 0);
3378533631
}
3378633632
#endif
3378733633
shell_main_exit:
3378833634
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -122,10 +122,13 @@
122 typedef sqlite3_int64 i64;
123 typedef sqlite3_uint64 u64;
124 typedef unsigned char u8;
125 #include <ctype.h>
126 #include <stdarg.h>
 
 
 
127
128 #if !defined(_WIN32) && !defined(WIN32)
129 # include <signal.h>
130 # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
131 # include <pwd.h>
@@ -646,12 +649,23 @@
646 if( a==0 ) a = "";
647 if( b==0 ) b = "";
648 return strncmp(a,b,n);
649 }
650
651 /* Return the current wall-clock time */
 
 
652 static sqlite3_int64 timeOfDay(void){
 
 
 
 
 
 
 
 
 
653 static sqlite3_vfs *clockVfs = 0;
654 sqlite3_int64 t;
655 if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
656 if( clockVfs==0 ) return 0; /* Never actually happens */
657 if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
@@ -659,11 +673,16 @@
659 }else{
660 double r;
661 clockVfs->xCurrentTime(clockVfs, &r);
662 t = (sqlite3_int64)(r*86400000.0);
663 }
664 return t;
 
 
 
 
 
665 }
666
667 #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
668 #include <sys/time.h>
669 #include <sys/resource.h>
@@ -704,12 +723,12 @@
704 static void endTimer(FILE *out){
705 if( enableTimer ){
706 sqlite3_int64 iEnd = timeOfDay();
707 struct rusage sEnd;
708 getrusage(RUSAGE_SELF, &sEnd);
709 sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
710 (iEnd - iBegin)*0.001,
711 timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
712 timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
713 }
714 }
715
@@ -783,14 +802,23 @@
783 static void endTimer(FILE *out){
784 if( enableTimer && getProcessTimesAddr){
785 FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
786 sqlite3_int64 ftWallEnd = timeOfDay();
787 getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
788 sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
789 (ftWallEnd - ftWallBegin)*0.001,
 
 
 
 
 
 
 
 
790 timeDiff(&ftUserBegin, &ftUserEnd),
791 timeDiff(&ftKernelBegin, &ftKernelEnd));
 
792 }
793 }
794
795 #define BEGIN_TIMER beginTimer()
796 #define END_TIMER(X) endTimer(X)
@@ -1126,11 +1154,11 @@
1126 }
1127 if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80
1128 && (z[3] & 0xc0)==0x80
1129 ){
1130 *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
1131 | (z[4] & 0x3f);
1132 return 4;
1133 }
1134 *pU = 0;
1135 return 1;
1136 }
@@ -1189,18 +1217,28 @@
1189 ** since with %*.*s the width is measured in bytes, not characters.
1190 **
1191 ** Take into account zero-width and double-width Unicode characters.
1192 ** In other words, a zero-width character does not count toward the
1193 ** the w limit. A double-width character counts as two.
 
 
 
1194 */
1195 static void utf8_width_print(FILE *out, int w, const char *zUtf){
1196 const unsigned char *a = (const unsigned char*)zUtf;
 
1197 unsigned char c;
1198 int i = 0;
1199 int n = 0;
1200 int k;
1201 int aw = w<0 ? -w : w;
 
 
 
 
 
 
1202 if( zUtf==0 ) zUtf = "";
1203 while( (c = a[i])!=0 ){
1204 if( (c&0xc0)==0xc0 ){
1205 int u;
1206 int len = decodeUtf8(a+i, &u);
@@ -1323,11 +1361,11 @@
1323
1324 /*
1325 ** This routine reads a line of text from FILE in, stores
1326 ** the text in memory obtained from malloc() and returns a pointer
1327 ** to the text. NULL is returned at end of file, or if malloc()
1328 ** fails.
1329 **
1330 ** If zLine is not NULL then it is a malloced buffer returned from
1331 ** a previous call to this routine that may be reused.
1332 */
1333 static char *local_getline(char *zLine, FILE *in){
@@ -1334,10 +1372,14 @@
1334 int nLine = zLine==0 ? 0 : 100;
1335 int n = 0;
1336
1337 while( 1 ){
1338 if( n+100>nLine ){
 
 
 
 
1339 nLine = nLine*2 + 100;
1340 zLine = realloc(zLine, nLine);
1341 shell_check_oom(zLine);
1342 }
1343 if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
@@ -1417,14 +1459,18 @@
1417 return -1;
1418 }
1419
1420 /*
1421 ** Interpret zArg as an integer value, possibly with suffixes.
 
 
 
 
1422 */
1423 static sqlite3_int64 integerValue(const char *zArg){
1424 sqlite3_int64 v = 0;
1425 static const struct { char *zSuffix; int iMult; } aMult[] = {
1426 { "KiB", 1024 },
1427 { "MiB", 1024*1024 },
1428 { "GiB", 1024*1024*1024 },
1429 { "KB", 1000 },
1430 { "MB", 1000000 },
@@ -1443,46 +1489,54 @@
1443 }
1444 if( zArg[0]=='0' && zArg[1]=='x' ){
1445 int x;
1446 zArg += 2;
1447 while( (x = hexDigitValue(zArg[0]))>=0 ){
 
1448 v = (v<<4) + x;
1449 zArg++;
1450 }
1451 }else{
1452 while( IsDigit(zArg[0]) ){
1453 v = v*10 + zArg[0] - '0';
 
 
 
1454 zArg++;
1455 }
1456 }
1457 for(i=0; i<ArraySize(aMult); i++){
1458 if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
 
1459 v *= aMult[i].iMult;
1460 break;
1461 }
1462 }
1463 return isNeg? -v : v;
 
 
 
1464 }
1465
1466 /*
1467 ** A variable length string to which one can append text.
1468 */
1469 typedef struct ShellText ShellText;
1470 struct ShellText {
1471 char *z;
1472 int n;
1473 int nAlloc;
1474 };
1475
1476 /*
1477 ** Initialize and destroy a ShellText object
1478 */
1479 static void initText(ShellText *p){
1480 memset(p, 0, sizeof(*p));
1481 }
1482 static void freeText(ShellText *p){
1483 free(p->z);
1484 initText(p);
1485 }
1486
1487 /* zIn is either a pointer to a NULL-terminated string in memory obtained
1488 ** from malloc(), or a NULL pointer. The string pointed to by zAppend is
@@ -1503,30 +1557,30 @@
1503 for(i=0; i<nAppend; i++){
1504 if( zAppend[i]==quote ) len++;
1505 }
1506 }
1507
1508 if( p->z==0 || p->n+len>=p->nAlloc ){
1509 p->nAlloc = p->nAlloc*2 + len + 20;
1510 p->z = realloc(p->z, p->nAlloc);
1511 shell_check_oom(p->z);
1512 }
1513
1514 if( quote ){
1515 char *zCsr = p->z+p->n;
1516 *zCsr++ = quote;
1517 for(i=0; i<nAppend; i++){
1518 *zCsr++ = zAppend[i];
1519 if( zAppend[i]==quote ) *zCsr++ = quote;
1520 }
1521 *zCsr++ = quote;
1522 p->n = (int)(zCsr - p->z);
1523 *zCsr = '\0';
1524 }else{
1525 memcpy(p->z+p->n, zAppend, nAppend);
1526 p->n += nAppend;
1527 p->z[p->n] = '\0';
1528 }
1529 }
1530
1531 /*
1532 ** Attempt to determine if identifier zName needs to be quoted, either
@@ -1547,10 +1601,13 @@
1547 }
1548
1549 /*
1550 ** Construct a fake object name and column list to describe the structure
1551 ** of the view, virtual table, or table valued function zSchema.zName.
 
 
 
1552 */
1553 static char *shellFakeSchema(
1554 sqlite3 *db, /* The database connection containing the vtab */
1555 const char *zSchema, /* Schema of the database holding the vtab */
1556 const char *zName /* The name of the virtual table */
@@ -1587,13 +1644,13 @@
1587 }
1588 appendText(&s, ")", 0);
1589 sqlite3_finalize(pStmt);
1590 if( nRow==0 ){
1591 freeText(&s);
1592 s.z = 0;
1593 }
1594 return s.z;
1595 }
1596
1597 /*
1598 ** SQL function: strtod(X)
1599 **
@@ -1692,11 +1749,11 @@
1692 if( z==0 ){
1693 z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
1694 }else{
1695 z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
1696 }
1697 free(zFake);
1698 }
1699 if( z ){
1700 sqlite3_result_text(pCtx, z, -1, sqlite3_free);
1701 return;
1702 }
@@ -3667,11 +3724,12 @@
3667 iExp -= p->nFrac;
3668 p->nFrac = 0;
3669 }
3670 }
3671 if( iExp>0 ){
3672 p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
 
3673 if( p->a==0 ) goto new_from_text_failed;
3674 memset(p->a+p->nDigit, 0, iExp);
3675 p->nDigit += iExp;
3676 }
3677 }else if( iExp<0 ){
@@ -3686,18 +3744,23 @@
3686 iExp -= nExtra;
3687 p->nFrac = p->nDigit - 1;
3688 }
3689 }
3690 if( iExp>0 ){
3691 p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
 
3692 if( p->a==0 ) goto new_from_text_failed;
3693 memmove(p->a+iExp, p->a, p->nDigit);
3694 memset(p->a, 0, iExp);
3695 p->nDigit += iExp;
3696 p->nFrac += iExp;
3697 }
3698 }
 
 
 
 
3699 return p;
3700
3701 new_from_text_failed:
3702 if( p ){
3703 if( p->a ) sqlite3_free(p->a);
@@ -3786,11 +3849,11 @@
3786 }
3787 if( p->isNull ){
3788 sqlite3_result_null(pCtx);
3789 return;
3790 }
3791 z = sqlite3_malloc( p->nDigit+4 );
3792 if( z==0 ){
3793 sqlite3_result_error_nomem(pCtx);
3794 return;
3795 }
3796 i = 0;
@@ -3851,11 +3914,11 @@
3851 }
3852 for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
3853 for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
3854 nFrac = p->nFrac + (nDigit - p->nDigit);
3855 nDigit -= nZero;
3856 z = sqlite3_malloc( nDigit+20 );
3857 if( z==0 ){
3858 sqlite3_result_error_nomem(pCtx);
3859 return;
3860 }
3861 if( nDigit==0 ){
@@ -3896,17 +3959,25 @@
3896 ** pA!=0
3897 ** pA->isNull==0
3898 ** pB!=0
3899 ** pB->isNull==0
3900 */
3901 static int decimal_cmp(const Decimal *pA, const Decimal *pB){
3902 int nASig, nBSig, rc, n;
 
 
 
 
 
 
 
 
3903 if( pA->sign!=pB->sign ){
3904 return pA->sign ? -1 : +1;
3905 }
3906 if( pA->sign ){
3907 const Decimal *pTemp = pA;
3908 pA = pB;
3909 pB = pTemp;
3910 }
3911 nASig = pA->nDigit - pA->nFrac;
3912 nBSig = pB->nDigit - pB->nFrac;
@@ -4064,11 +4135,12 @@
4064 if( pA==0 || pA->oom || pA->isNull
4065 || pB==0 || pB->oom || pB->isNull
4066 ){
4067 goto mul_end;
4068 }
4069 acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
 
4070 if( acc==0 ){
4071 pA->oom = 1;
4072 goto mul_end;
4073 }
4074 memset(acc, 0, pA->nDigit + pB->nDigit + 2);
@@ -4151,11 +4223,11 @@
4151 r = -r;
4152 }else{
4153 isNeg = 0;
4154 }
4155 memcpy(&a,&r,sizeof(a));
4156 if( a==0 ){
4157 e = 0;
4158 m = 0;
4159 }else{
4160 e = a>>52;
4161 m = a & ((((sqlite3_int64)1)<<52)-1);
@@ -4426,516 +4498,10 @@
4426 }
4427 return rc;
4428 }
4429
4430 /************************* End ../ext/misc/decimal.c ********************/
4431 /************************* Begin ../ext/misc/percentile.c ******************/
4432 /*
4433 ** 2013-05-28
4434 **
4435 ** The author disclaims copyright to this source code. In place of
4436 ** a legal notice, here is a blessing:
4437 **
4438 ** May you do good and not evil.
4439 ** May you find forgiveness for yourself and forgive others.
4440 ** May you share freely, never taking more than you give.
4441 **
4442 ******************************************************************************
4443 **
4444 ** This file contains code to implement the percentile(Y,P) SQL function
4445 ** and similar as described below:
4446 **
4447 ** (1) The percentile(Y,P) function is an aggregate function taking
4448 ** exactly two arguments.
4449 **
4450 ** (2) If the P argument to percentile(Y,P) is not the same for every
4451 ** row in the aggregate then an error is thrown. The word "same"
4452 ** in the previous sentence means that the value differ by less
4453 ** than 0.001.
4454 **
4455 ** (3) If the P argument to percentile(Y,P) evaluates to anything other
4456 ** than a number in the range of 0.0 to 100.0 inclusive then an
4457 ** error is thrown.
4458 **
4459 ** (4) If any Y argument to percentile(Y,P) evaluates to a value that
4460 ** is not NULL and is not numeric then an error is thrown.
4461 **
4462 ** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
4463 ** infinity then an error is thrown. (SQLite always interprets NaN
4464 ** values as NULL.)
4465 **
4466 ** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
4467 ** including CASE WHEN expressions.
4468 **
4469 ** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
4470 ** one million (1,000,000) rows.
4471 **
4472 ** (8) If there are no non-NULL values for Y, then percentile(Y,P)
4473 ** returns NULL.
4474 **
4475 ** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
4476 ** returns the one Y value.
4477 **
4478 ** (10) If there N non-NULL values of Y where N is two or more and
4479 ** the Y values are ordered from least to greatest and a graph is
4480 ** drawn from 0 to N-1 such that the height of the graph at J is
4481 ** the J-th Y value and such that straight lines are drawn between
4482 ** adjacent Y values, then the percentile(Y,P) function returns
4483 ** the height of the graph at P*(N-1)/100.
4484 **
4485 ** (11) The percentile(Y,P) function always returns either a floating
4486 ** point number or NULL.
4487 **
4488 ** (12) The percentile(Y,P) is implemented as a single C99 source-code
4489 ** file that compiles into a shared-library or DLL that can be loaded
4490 ** into SQLite using the sqlite3_load_extension() interface.
4491 **
4492 ** (13) A separate median(Y) function is the equivalent percentile(Y,50).
4493 **
4494 ** (14) A separate percentile_cont(Y,P) function is equivalent to
4495 ** percentile(Y,P/100.0). In other words, the fraction value in
4496 ** the second argument is in the range of 0 to 1 instead of 0 to 100.
4497 **
4498 ** (15) A separate percentile_disc(Y,P) function is like
4499 ** percentile_cont(Y,P) except that instead of returning the weighted
4500 ** average of the nearest two input values, it returns the next lower
4501 ** value. So the percentile_disc(Y,P) will always return a value
4502 ** that was one of the inputs.
4503 **
4504 ** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
4505 ** percentile_disc(Y,P) can be used as window functions.
4506 **
4507 ** Differences from standard SQL:
4508 **
4509 ** * The percentile_cont(X,P) function is equivalent to the following in
4510 ** standard SQL:
4511 **
4512 ** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
4513 **
4514 ** The SQLite syntax is much more compact. The standard SQL syntax
4515 ** is also supported if SQLite is compiled with the
4516 ** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
4517 **
4518 ** * No median(X) function exists in the SQL standard. App developers
4519 ** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
4520 **
4521 ** * No percentile(Y,P) function exists in the SQL standard. Instead of
4522 ** percential(Y,P), developers must write this:
4523 ** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
4524 ** the fraction parameter to percentile() goes from 0 to 100 whereas
4525 ** the fraction parameter in SQL standard percentile_cont() goes from
4526 ** 0 to 1.
4527 **
4528 ** Implementation notes as of 2024-08-31:
4529 **
4530 ** * The regular aggregate-function versions of these routines work
4531 ** by accumulating all values in an array of doubles, then sorting
4532 ** that array using quicksort before computing the answer. Thus
4533 ** the runtime is O(NlogN) where N is the number of rows of input.
4534 **
4535 ** * For the window-function versions of these routines, the array of
4536 ** inputs is sorted as soon as the first value is computed. Thereafter,
4537 ** the array is kept in sorted order using an insert-sort. This
4538 ** results in O(N*K) performance where K is the size of the window.
4539 ** One can imagine alternative implementations that give O(N*logN*logK)
4540 ** performance, but they require more complex logic and data structures.
4541 ** The developers have elected to keep the asymptotically slower
4542 ** algorithm for now, for simplicity, under the theory that window
4543 ** functions are seldom used and when they are, the window size K is
4544 ** often small. The developers might revisit that decision later,
4545 ** should the need arise.
4546 */
4547 #if defined(SQLITE3_H)
4548 /* no-op */
4549 #elif defined(SQLITE_STATIC_PERCENTILE)
4550 /* # include "sqlite3.h" */
4551 #else
4552 /* # include "sqlite3ext.h" */
4553 SQLITE_EXTENSION_INIT1
4554 #endif
4555 #include <assert.h>
4556 #include <string.h>
4557 #include <stdlib.h>
4558
4559 /* The following object is the group context for a single percentile()
4560 ** aggregate. Remember all input Y values until the very end.
4561 ** Those values are accumulated in the Percentile.a[] array.
4562 */
4563 typedef struct Percentile Percentile;
4564 struct Percentile {
4565 unsigned nAlloc; /* Number of slots allocated for a[] */
4566 unsigned nUsed; /* Number of slots actually used in a[] */
4567 char bSorted; /* True if a[] is already in sorted order */
4568 char bKeepSorted; /* True if advantageous to keep a[] sorted */
4569 char bPctValid; /* True if rPct is valid */
4570 double rPct; /* Fraction. 0.0 to 1.0 */
4571 double *a; /* Array of Y values */
4572 };
4573
4574 /* Details of each function in the percentile family */
4575 typedef struct PercentileFunc PercentileFunc;
4576 struct PercentileFunc {
4577 const char *zName; /* Function name */
4578 char nArg; /* Number of arguments */
4579 char mxFrac; /* Maximum value of the "fraction" input */
4580 char bDiscrete; /* True for percentile_disc() */
4581 };
4582 static const PercentileFunc aPercentFunc[] = {
4583 { "median", 1, 1, 0 },
4584 { "percentile", 2, 100, 0 },
4585 { "percentile_cont", 2, 1, 0 },
4586 { "percentile_disc", 2, 1, 1 },
4587 };
4588
4589 /*
4590 ** Return TRUE if the input floating-point number is an infinity.
4591 */
4592 static int percentIsInfinity(double r){
4593 sqlite3_uint64 u;
4594 assert( sizeof(u)==sizeof(r) );
4595 memcpy(&u, &r, sizeof(u));
4596 return ((u>>52)&0x7ff)==0x7ff;
4597 }
4598
4599 /*
4600 ** Return TRUE if two doubles differ by 0.001 or less.
4601 */
4602 static int percentSameValue(double a, double b){
4603 a -= b;
4604 return a>=-0.001 && a<=0.001;
4605 }
4606
4607 /*
4608 ** Search p (which must have p->bSorted) looking for an entry with
4609 ** value y. Return the index of that entry.
4610 **
4611 ** If bExact is true, return -1 if the entry is not found.
4612 **
4613 ** If bExact is false, return the index at which a new entry with
4614 ** value y should be insert in order to keep the values in sorted
4615 ** order. The smallest return value in this case will be 0, and
4616 ** the largest return value will be p->nUsed.
4617 */
4618 static int percentBinarySearch(Percentile *p, double y, int bExact){
4619 int iFirst = 0; /* First element of search range */
4620 int iLast = p->nUsed - 1; /* Last element of search range */
4621 while( iLast>=iFirst ){
4622 int iMid = (iFirst+iLast)/2;
4623 double x = p->a[iMid];
4624 if( x<y ){
4625 iFirst = iMid + 1;
4626 }else if( x>y ){
4627 iLast = iMid - 1;
4628 }else{
4629 return iMid;
4630 }
4631 }
4632 if( bExact ) return -1;
4633 return iFirst;
4634 }
4635
4636 /*
4637 ** Generate an error for a percentile function.
4638 **
4639 ** The error format string must have exactly one occurrence of "%%s()"
4640 ** (with two '%' characters). That substring will be replaced by the name
4641 ** of the function.
4642 */
4643 static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
4644 PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
4645 char *zMsg1;
4646 char *zMsg2;
4647 va_list ap;
4648
4649 va_start(ap, zFormat);
4650 zMsg1 = sqlite3_vmprintf(zFormat, ap);
4651 va_end(ap);
4652 zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0;
4653 sqlite3_result_error(pCtx, zMsg2, -1);
4654 sqlite3_free(zMsg1);
4655 sqlite3_free(zMsg2);
4656 }
4657
4658 /*
4659 ** The "step" function for percentile(Y,P) is called once for each
4660 ** input row.
4661 */
4662 static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
4663 Percentile *p;
4664 double rPct;
4665 int eType;
4666 double y;
4667 assert( argc==2 || argc==1 );
4668
4669 if( argc==1 ){
4670 /* Requirement 13: median(Y) is the same as percentile(Y,50). */
4671 rPct = 0.5;
4672 }else{
4673 /* Requirement 3: P must be a number between 0 and 100 */
4674 PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
4675 eType = sqlite3_value_numeric_type(argv[1]);
4676 rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac;
4677 if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
4678 || rPct<0.0 || rPct>1.0
4679 ){
4680 percentError(pCtx, "the fraction argument to %%s()"
4681 " is not between 0.0 and %.1f",
4682 (double)pFunc->mxFrac);
4683 return;
4684 }
4685 }
4686
4687 /* Allocate the session context. */
4688 p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
4689 if( p==0 ) return;
4690
4691 /* Remember the P value. Throw an error if the P value is different
4692 ** from any prior row, per Requirement (2). */
4693 if( !p->bPctValid ){
4694 p->rPct = rPct;
4695 p->bPctValid = 1;
4696 }else if( !percentSameValue(p->rPct,rPct) ){
4697 percentError(pCtx, "the fraction argument to %%s()"
4698 " is not the same for all input rows");
4699 return;
4700 }
4701
4702 /* Ignore rows for which Y is NULL */
4703 eType = sqlite3_value_type(argv[0]);
4704 if( eType==SQLITE_NULL ) return;
4705
4706 /* If not NULL, then Y must be numeric. Otherwise throw an error.
4707 ** Requirement 4 */
4708 if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
4709 percentError(pCtx, "input to %%s() is not numeric");
4710 return;
4711 }
4712
4713 /* Throw an error if the Y value is infinity or NaN */
4714 y = sqlite3_value_double(argv[0]);
4715 if( percentIsInfinity(y) ){
4716 percentError(pCtx, "Inf input to %%s()");
4717 return;
4718 }
4719
4720 /* Allocate and store the Y */
4721 if( p->nUsed>=p->nAlloc ){
4722 unsigned n = p->nAlloc*2 + 250;
4723 double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
4724 if( a==0 ){
4725 sqlite3_free(p->a);
4726 memset(p, 0, sizeof(*p));
4727 sqlite3_result_error_nomem(pCtx);
4728 return;
4729 }
4730 p->nAlloc = n;
4731 p->a = a;
4732 }
4733 if( p->nUsed==0 ){
4734 p->a[p->nUsed++] = y;
4735 p->bSorted = 1;
4736 }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
4737 p->a[p->nUsed++] = y;
4738 }else if( p->bKeepSorted ){
4739 int i;
4740 i = percentBinarySearch(p, y, 0);
4741 if( i<(int)p->nUsed ){
4742 memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
4743 }
4744 p->a[i] = y;
4745 p->nUsed++;
4746 }else{
4747 p->a[p->nUsed++] = y;
4748 p->bSorted = 0;
4749 }
4750 }
4751
4752 /*
4753 ** Interchange two doubles.
4754 */
4755 #define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
4756
4757 /*
4758 ** Sort an array of doubles.
4759 **
4760 ** Algorithm: quicksort
4761 **
4762 ** This is implemented separately rather than using the qsort() routine
4763 ** from the standard library because:
4764 **
4765 ** (1) To avoid a dependency on qsort()
4766 ** (2) To avoid the function call to the comparison routine for each
4767 ** comparison.
4768 */
4769 static void percentSort(double *a, unsigned int n){
4770 int iLt; /* Entries before a[iLt] are less than rPivot */
4771 int iGt; /* Entries at or after a[iGt] are greater than rPivot */
4772 int i; /* Loop counter */
4773 double rPivot; /* The pivot value */
4774
4775 assert( n>=2 );
4776 if( a[0]>a[n-1] ){
4777 SWAP_DOUBLE(a[0],a[n-1])
4778 }
4779 if( n==2 ) return;
4780 iGt = n-1;
4781 i = n/2;
4782 if( a[0]>a[i] ){
4783 SWAP_DOUBLE(a[0],a[i])
4784 }else if( a[i]>a[iGt] ){
4785 SWAP_DOUBLE(a[i],a[iGt])
4786 }
4787 if( n==3 ) return;
4788 rPivot = a[i];
4789 iLt = i = 1;
4790 do{
4791 if( a[i]<rPivot ){
4792 if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
4793 iLt++;
4794 i++;
4795 }else if( a[i]>rPivot ){
4796 do{
4797 iGt--;
4798 }while( iGt>i && a[iGt]>rPivot );
4799 SWAP_DOUBLE(a[i],a[iGt])
4800 }else{
4801 i++;
4802 }
4803 }while( i<iGt );
4804 if( iLt>=2 ) percentSort(a, iLt);
4805 if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
4806
4807 /* Uncomment for testing */
4808 #if 0
4809 for(i=0; i<n-1; i++){
4810 assert( a[i]<=a[i+1] );
4811 }
4812 #endif
4813 }
4814
4815
4816 /*
4817 ** The "inverse" function for percentile(Y,P) is called to remove a
4818 ** row that was previously inserted by "step".
4819 */
4820 static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
4821 Percentile *p;
4822 int eType;
4823 double y;
4824 int i;
4825 assert( argc==2 || argc==1 );
4826
4827 /* Allocate the session context. */
4828 p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
4829 assert( p!=0 );
4830
4831 /* Ignore rows for which Y is NULL */
4832 eType = sqlite3_value_type(argv[0]);
4833 if( eType==SQLITE_NULL ) return;
4834
4835 /* If not NULL, then Y must be numeric. Otherwise throw an error.
4836 ** Requirement 4 */
4837 if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
4838 return;
4839 }
4840
4841 /* Ignore the Y value if it is infinity or NaN */
4842 y = sqlite3_value_double(argv[0]);
4843 if( percentIsInfinity(y) ){
4844 return;
4845 }
4846 if( p->bSorted==0 ){
4847 assert( p->nUsed>1 );
4848 percentSort(p->a, p->nUsed);
4849 p->bSorted = 1;
4850 }
4851 p->bKeepSorted = 1;
4852
4853 /* Find and remove the row */
4854 i = percentBinarySearch(p, y, 1);
4855 if( i>=0 ){
4856 p->nUsed--;
4857 if( i<(int)p->nUsed ){
4858 memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
4859 }
4860 }
4861 }
4862
4863 /*
4864 ** Compute the final output of percentile(). Clean up all allocated
4865 ** memory if and only if bIsFinal is true.
4866 */
4867 static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
4868 Percentile *p;
4869 PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
4870 unsigned i1, i2;
4871 double v1, v2;
4872 double ix, vx;
4873 p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
4874 if( p==0 ) return;
4875 if( p->a==0 ) return;
4876 if( p->nUsed ){
4877 if( p->bSorted==0 ){
4878 assert( p->nUsed>1 );
4879 percentSort(p->a, p->nUsed);
4880 p->bSorted = 1;
4881 }
4882 ix = p->rPct*(p->nUsed-1);
4883 i1 = (unsigned)ix;
4884 if( pFunc->bDiscrete ){
4885 vx = p->a[i1];
4886 }else{
4887 i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
4888 v1 = p->a[i1];
4889 v2 = p->a[i2];
4890 vx = v1 + (v2-v1)*(ix-i1);
4891 }
4892 sqlite3_result_double(pCtx, vx);
4893 }
4894 if( bIsFinal ){
4895 sqlite3_free(p->a);
4896 memset(p, 0, sizeof(*p));
4897 }else{
4898 p->bKeepSorted = 1;
4899 }
4900 }
4901 static void percentFinal(sqlite3_context *pCtx){
4902 percentCompute(pCtx, 1);
4903 }
4904 static void percentValue(sqlite3_context *pCtx){
4905 percentCompute(pCtx, 0);
4906 }
4907
4908 #if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE)
4909
4910 #endif
4911 int sqlite3_percentile_init(
4912 sqlite3 *db,
4913 char **pzErrMsg,
4914 const sqlite3_api_routines *pApi
4915 ){
4916 int rc = SQLITE_OK;
4917 unsigned int i;
4918 #ifdef SQLITE3EXT_H
4919 SQLITE_EXTENSION_INIT2(pApi);
4920 #else
4921 (void)pApi; /* Unused parameter */
4922 #endif
4923 (void)pzErrMsg; /* Unused parameter */
4924 for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
4925 rc = sqlite3_create_window_function(db,
4926 aPercentFunc[i].zName,
4927 aPercentFunc[i].nArg,
4928 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
4929 (void*)&aPercentFunc[i],
4930 percentStep, percentFinal, percentValue, percentInverse, 0);
4931 if( rc ) break;
4932 }
4933 return rc;
4934 }
4935
4936 /************************* End ../ext/misc/percentile.c ********************/
4937 #undef sqlite3_base_init
4938 #define sqlite3_base_init sqlite3_base64_init
4939 /************************* Begin ../ext/misc/base64.c ******************/
4940 /*
4941 ** 2022-11-18
@@ -5144,20 +4710,22 @@
5144 return pOut;
5145 }
5146
5147 /* This function does the work for the SQLite base64(x) UDF. */
5148 static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
5149 int nb, nc, nv = sqlite3_value_bytes(av[0]);
 
 
5150 int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
5151 SQLITE_LIMIT_LENGTH, -1);
5152 char *cBuf;
5153 u8 *bBuf;
5154 assert(na==1);
5155 switch( sqlite3_value_type(av[0]) ){
5156 case SQLITE_BLOB:
5157 nb = nv;
5158 nc = 4*(nv+2/3); /* quads needed */
5159 nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
5160 if( nvMax < nc ){
5161 sqlite3_result_error(context, "blob expanded to base64 too big", -1);
5162 return;
5163 }
@@ -5524,11 +5092,11 @@
5524 }
5525 # endif
5526
5527 /* This function does the work for the SQLite base85(x) UDF. */
5528 static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
5529 int nb, nc, nv = sqlite3_value_bytes(av[0]);
5530 int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
5531 SQLITE_LIMIT_LENGTH, -1);
5532 char *cBuf;
5533 u8 *bBuf;
5534 assert(na==1);
@@ -5829,10 +5397,13 @@
5829 }
5830 memcpy(&a,&r,sizeof(a));
5831 if( a==0 ){
5832 e = 0;
5833 m = 0;
 
 
 
5834 }else{
5835 e = a>>52;
5836 m = a & ((((sqlite3_int64)1)<<52)-1);
5837 if( e==0 ){
5838 m <<= 1;
@@ -6052,23 +5623,24 @@
6052 ** Examples:
6053 **
6054 ** SELECT * FROM generate_series(0,100,5);
6055 **
6056 ** The query above returns integers from 0 through 100 counting by steps
6057 ** of 5.
 
6058 **
6059 ** SELECT * FROM generate_series(0,100);
6060 **
6061 ** Integers from 0 through 100 with a step size of 1.
6062 **
6063 ** SELECT * FROM generate_series(20) LIMIT 10;
6064 **
6065 ** Integers 20 through 29.
6066 **
6067 ** SELECT * FROM generate_series(0,-100,-5);
6068 **
6069 ** Integers 0 -5 -10 ... -100.
6070 **
6071 ** SELECT * FROM generate_series(0,-1);
6072 **
6073 ** Empty sequence.
6074 **
@@ -6140,143 +5712,92 @@
6140 #include <string.h>
6141 #include <limits.h>
6142 #include <math.h>
6143
6144 #ifndef SQLITE_OMIT_VIRTUALTABLE
6145 /*
6146 ** Return that member of a generate_series(...) sequence whose 0-based
6147 ** index is ix. The 0th member is given by smBase. The sequence members
6148 ** progress per ix increment by smStep.
6149 */
6150 static sqlite3_int64 genSeqMember(
6151 sqlite3_int64 smBase,
6152 sqlite3_int64 smStep,
6153 sqlite3_uint64 ix
6154 ){
6155 static const sqlite3_uint64 mxI64 =
6156 ((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
6157 if( ix>=mxI64 ){
6158 /* Get ix into signed i64 range. */
6159 ix -= mxI64;
6160 /* With 2's complement ALU, this next can be 1 step, but is split into
6161 * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
6162 smBase += (mxI64/2) * smStep;
6163 smBase += (mxI64 - mxI64/2) * smStep;
6164 }
6165 /* Under UBSAN (or on 1's complement machines), must do this last term
6166 * in steps to avoid the dreaded (and harmless) signed multiply overflow. */
6167 if( ix>=2 ){
6168 sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
6169 smBase += ix2*smStep;
6170 ix -= ix2;
6171 }
6172 return smBase + ((sqlite3_int64)ix)*smStep;
6173 }
6174
6175 /* typedef unsigned char u8; */
6176
6177 typedef struct SequenceSpec {
6178 sqlite3_int64 iOBase; /* Original starting value ("start") */
6179 sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
6180 sqlite3_int64 iBase; /* Starting value to actually use */
6181 sqlite3_int64 iTerm; /* Terminal value to actually use */
6182 sqlite3_int64 iStep; /* Increment ("step") */
6183 sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
6184 sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
6185 sqlite3_int64 iValueNow; /* Current value during generation */
6186 u8 isNotEOF; /* Sequence generation not exhausted */
6187 u8 isReversing; /* Sequence is being reverse generated */
6188 } SequenceSpec;
6189
6190 /*
6191 ** Prepare a SequenceSpec for use in generating an integer series
6192 ** given initialized iBase, iTerm and iStep values. Sequence is
6193 ** initialized per given isReversing. Other members are computed.
6194 */
6195 static void setupSequence( SequenceSpec *pss ){
6196 int bSameSigns;
6197 pss->uSeqIndexMax = 0;
6198 pss->isNotEOF = 0;
6199 bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
6200 if( pss->iTerm < pss->iBase ){
6201 sqlite3_uint64 nuspan = 0;
6202 if( bSameSigns ){
6203 nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
6204 }else{
6205 /* Under UBSAN (or on 1's complement machines), must do this in steps.
6206 * In this clause, iBase>=0 and iTerm<0 . */
6207 nuspan = 1;
6208 nuspan += pss->iBase;
6209 nuspan += -(pss->iTerm+1);
6210 }
6211 if( pss->iStep<0 ){
6212 pss->isNotEOF = 1;
6213 if( nuspan==ULONG_MAX ){
6214 pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1;
6215 }else if( pss->iStep>LLONG_MIN ){
6216 pss->uSeqIndexMax = nuspan/-pss->iStep;
6217 }
6218 }
6219 }else if( pss->iTerm > pss->iBase ){
6220 sqlite3_uint64 puspan = 0;
6221 if( bSameSigns ){
6222 puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
6223 }else{
6224 /* Under UBSAN (or on 1's complement machines), must do this in steps.
6225 * In this clause, iTerm>=0 and iBase<0 . */
6226 puspan = 1;
6227 puspan += pss->iTerm;
6228 puspan += -(pss->iBase+1);
6229 }
6230 if( pss->iStep>0 ){
6231 pss->isNotEOF = 1;
6232 pss->uSeqIndexMax = puspan/pss->iStep;
6233 }
6234 }else if( pss->iTerm == pss->iBase ){
6235 pss->isNotEOF = 1;
6236 pss->uSeqIndexMax = 0;
6237 }
6238 pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0;
6239 pss->iValueNow = (pss->isReversing)
6240 ? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax)
6241 : pss->iBase;
6242 }
6243
6244 /*
6245 ** Progress sequence generator to yield next value, if any.
6246 ** Leave its state to either yield next value or be at EOF.
6247 ** Return whether there is a next value, or 0 at EOF.
6248 */
6249 static int progressSequence( SequenceSpec *pss ){
6250 if( !pss->isNotEOF ) return 0;
6251 if( pss->isReversing ){
6252 if( pss->uSeqIndexNow > 0 ){
6253 pss->uSeqIndexNow--;
6254 pss->iValueNow -= pss->iStep;
6255 }else{
6256 pss->isNotEOF = 0;
6257 }
6258 }else{
6259 if( pss->uSeqIndexNow < pss->uSeqIndexMax ){
6260 pss->uSeqIndexNow++;
6261 pss->iValueNow += pss->iStep;
6262 }else{
6263 pss->isNotEOF = 0;
6264 }
6265 }
6266 return pss->isNotEOF;
6267 }
6268
6269 /* series_cursor is a subclass of sqlite3_vtab_cursor which will
6270 ** serve as the underlying representation of a cursor that scans
6271 ** over rows of the result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6272 */
 
6273 typedef struct series_cursor series_cursor;
6274 struct series_cursor {
6275 sqlite3_vtab_cursor base; /* Base class - must be first */
6276 SequenceSpec ss; /* (this) Derived class data */
 
 
 
 
 
 
 
 
6277 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6278
6279 /*
6280 ** The seriesConnect() method is invoked to create a new
6281 ** series_vtab that describes the generate_series virtual table.
6282 **
@@ -6354,11 +5875,19 @@
6354 /*
6355 ** Advance a series_cursor to its next row of output.
6356 */
6357 static int seriesNext(sqlite3_vtab_cursor *cur){
6358 series_cursor *pCur = (series_cursor*)cur;
6359 progressSequence( & pCur->ss );
 
 
 
 
 
 
 
 
6360 return SQLITE_OK;
6361 }
6362
6363 /*
6364 ** Return values of columns for the row at which the series_cursor
@@ -6370,41 +5899,41 @@
6370 int i /* Which column to return */
6371 ){
6372 series_cursor *pCur = (series_cursor*)cur;
6373 sqlite3_int64 x = 0;
6374 switch( i ){
6375 case SERIES_COLUMN_START: x = pCur->ss.iOBase; break;
6376 case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break;
6377 case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
6378 default: x = pCur->ss.iValueNow; break;
6379 }
6380 sqlite3_result_int64(ctx, x);
6381 return SQLITE_OK;
6382 }
6383
6384 #ifndef LARGEST_UINT64
6385 #define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
6386 #define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
6387 #define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
6388 #endif
6389
6390 /*
6391 ** The rowid is the same as the value.
6392 */
6393 static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
6394 series_cursor *pCur = (series_cursor*)cur;
6395 *pRowid = pCur->ss.iValueNow;
6396 return SQLITE_OK;
6397 }
6398
6399 /*
6400 ** Return TRUE if the cursor has been moved off of the last
6401 ** row of output.
6402 */
6403 static int seriesEof(sqlite3_vtab_cursor *cur){
6404 series_cursor *pCur = (series_cursor*)cur;
6405 return !pCur->ss.isNotEOF;
6406 }
6407
6408 /* True to cause run-time checking of the start=, stop=, and/or step=
6409 ** parameters. The only reason to do this is for testing the
6410 ** constraint checking logic for virtual tables in the SQLite core.
@@ -6411,10 +5940,63 @@
6411 */
6412 #ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
6413 # define SQLITE_SERIES_CONSTRAINT_VERIFY 0
6414 #endif
6415
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6416 /*
6417 ** This method is called to "rewind" the series_cursor object back
6418 ** to the first row of output. This method is always called at least
6419 ** once prior to any call to seriesColumn() or seriesRowid() or
6420 ** seriesEof().
@@ -6444,185 +6026,243 @@
6444 sqlite3_vtab_cursor *pVtabCursor,
6445 int idxNum, const char *idxStrUnused,
6446 int argc, sqlite3_value **argv
6447 ){
6448 series_cursor *pCur = (series_cursor *)pVtabCursor;
6449 int i = 0;
6450 int returnNoRows = 0;
6451 sqlite3_int64 iMin = SMALLEST_INT64;
6452 sqlite3_int64 iMax = LARGEST_INT64;
6453 sqlite3_int64 iLimit = 0;
6454 sqlite3_int64 iOffset = 0;
6455
6456 (void)idxStrUnused;
 
 
 
 
 
 
 
 
 
 
 
 
 
6457 if( idxNum & 0x01 ){
6458 pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
6459 }else{
6460 pCur->ss.iBase = 0;
6461 }
6462 if( idxNum & 0x02 ){
6463 pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
6464 }else{
6465 pCur->ss.iTerm = 0xffffffff;
6466 }
6467 if( idxNum & 0x04 ){
6468 pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
6469 if( pCur->ss.iStep==0 ){
6470 pCur->ss.iStep = 1;
6471 }else if( pCur->ss.iStep<0 ){
6472 if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
6473 }
6474 }else{
6475 pCur->ss.iStep = 1;
6476 }
6477
6478 /* If there are constraints on the value column but there are
6479 ** no constraints on the start, stop, and step columns, then
6480 ** initialize the default range to be the entire range of 64-bit signed
6481 ** integers. This range will contracted by the value column constraints
6482 ** further below.
6483 */
6484 if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
6485 pCur->ss.iBase = SMALLEST_INT64;
6486 }
6487 if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
6488 pCur->ss.iTerm = LARGEST_INT64;
6489 }
6490 pCur->ss.iOBase = pCur->ss.iBase;
6491 pCur->ss.iOTerm = pCur->ss.iTerm;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6492
6493 /* Extract the LIMIT and OFFSET values, but do not apply them yet.
6494 ** The range must first be constrained by the limits on value.
6495 */
6496 if( idxNum & 0x20 ){
6497 iLimit = sqlite3_value_int64(argv[i++]);
6498 if( idxNum & 0x40 ){
6499 iOffset = sqlite3_value_int64(argv[i++]);
6500 }
6501 }
6502
 
 
 
6503 if( idxNum & 0x3380 ){
6504 /* Extract the maximum range of output values determined by
6505 ** constraints on the "value" column.
6506 */
6507 if( idxNum & 0x0080 ){
6508 if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6509 double r = sqlite3_value_double(argv[i++]);
6510 if( r==ceil(r) ){
6511 iMin = iMax = (sqlite3_int64)r;
6512 }else{
6513 returnNoRows = 1;
6514 }
6515 }else{
6516 iMin = iMax = sqlite3_value_int64(argv[i++]);
6517 }
6518 }else{
6519 if( idxNum & 0x0300 ){
6520 if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6521 double r = sqlite3_value_double(argv[i++]);
6522 if( idxNum & 0x0200 && r==ceil(r) ){
6523 iMin = (sqlite3_int64)ceil(r+1.0);
6524 }else{
6525 iMin = (sqlite3_int64)ceil(r);
6526 }
6527 }else{
6528 iMin = sqlite3_value_int64(argv[i++]);
6529 if( idxNum & 0x0200 ){
 
 
6530 if( iMin==LARGEST_INT64 ){
6531 returnNoRows = 1;
6532 }else{
6533 iMin++;
6534 }
6535 }
6536 }
6537 }
6538 if( idxNum & 0x3000 ){
6539 if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6540 double r = sqlite3_value_double(argv[i++]);
6541 if( (idxNum & 0x2000)!=0 && r==floor(r) ){
 
 
6542 iMax = (sqlite3_int64)(r-1.0);
6543 }else{
6544 iMax = (sqlite3_int64)floor(r);
6545 }
6546 }else{
6547 iMax = sqlite3_value_int64(argv[i++]);
6548 if( idxNum & 0x2000 ){
6549 if( iMax==SMALLEST_INT64 ){
6550 returnNoRows = 1;
6551 }else{
6552 iMax--;
6553 }
6554 }
6555 }
6556 }
6557 if( iMin>iMax ){
6558 returnNoRows = 1;
6559 }
6560 }
6561
6562 /* Try to reduce the range of values to be generated based on
6563 ** constraints on the "value" column.
6564 */
6565 if( pCur->ss.iStep>0 ){
6566 sqlite3_int64 szStep = pCur->ss.iStep;
6567 if( pCur->ss.iBase<iMin ){
6568 sqlite3_uint64 d = iMin - pCur->ss.iBase;
6569 pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
6570 }
6571 if( pCur->ss.iTerm>iMax ){
6572 pCur->ss.iTerm = iMax;
 
 
 
 
 
6573 }
6574 }else{
6575 sqlite3_int64 szStep = -pCur->ss.iStep;
6576 assert( szStep>0 );
6577 if( pCur->ss.iBase>iMax ){
6578 sqlite3_uint64 d = pCur->ss.iBase - iMax;
6579 pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
6580 }
6581 if( pCur->ss.iTerm<iMin ){
6582 pCur->ss.iTerm = iMin;
 
 
 
 
6583 }
6584 }
6585 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6586
6587 /* Apply LIMIT and OFFSET constraints, if any */
 
6588 if( idxNum & 0x20 ){
6589 if( iOffset>0 ){
6590 pCur->ss.iBase += pCur->ss.iStep*iOffset;
6591 }
6592 if( iLimit>=0 ){
6593 sqlite3_int64 iTerm;
6594 iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
6595 if( pCur->ss.iStep<0 ){
6596 if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
6597 }else{
6598 if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
6599 }
6600 }
6601 }
6602
6603
6604 for(i=0; i<argc; i++){
6605 if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
6606 /* If any of the constraints have a NULL value, then return no rows.
6607 ** See ticket https://sqlite.org/src/info/fac496b61722daf2 */
6608 returnNoRows = 1;
6609 break;
6610 }
6611 }
6612 if( returnNoRows ){
6613 pCur->ss.iBase = 1;
6614 pCur->ss.iTerm = 0;
6615 pCur->ss.iStep = 1;
6616 }
6617 if( idxNum & 0x08 ){
6618 pCur->ss.isReversing = pCur->ss.iStep > 0;
6619 }else{
6620 pCur->ss.isReversing = pCur->ss.iStep < 0;
6621 }
6622 setupSequence( &pCur->ss );
6623 return SQLITE_OK;
 
 
 
 
 
 
 
 
6624 }
6625
6626 /*
6627 ** SQLite will invoke this method one or more times while planning a query
6628 ** that uses the generate_series virtual table. This routine needs to create
@@ -6948,10 +6588,12 @@
6948 ** expression and M is the size of the input string. The matcher never
6949 ** exhibits exponential behavior. Note that the X{p,q} operator expands
6950 ** to p copies of X following by q-p copies of X? and that the size of the
6951 ** regular expression in the O(N*M) performance bound is computed after
6952 ** this expansion.
 
 
6953 */
6954 #include <string.h>
6955 #include <stdlib.h>
6956 /* #include "sqlite3ext.h" */
6957 SQLITE_EXTENSION_INIT1
@@ -6988,36 +6630,10 @@
6988 #define RE_OP_NOTDIGIT 14 /* Not a digit */
6989 #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
6990 #define RE_OP_NOTSPACE 16 /* Not a digit */
6991 #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
6992 #define RE_OP_ATSTART 18 /* Currently at the start of the string */
6993
6994 #if defined(SQLITE_DEBUG)
6995 /* Opcode names used for symbolic debugging */
6996 static const char *ReOpName[] = {
6997 "EOF",
6998 "MATCH",
6999 "ANY",
7000 "ANYSTAR",
7001 "FORK",
7002 "GOTO",
7003 "ACCEPT",
7004 "CC_INC",
7005 "CC_EXC",
7006 "CC_VALUE",
7007 "CC_RANGE",
7008 "WORD",
7009 "NOTWORD",
7010 "DIGIT",
7011 "NOTDIGIT",
7012 "SPACE",
7013 "NOTSPACE",
7014 "BOUNDARY",
7015 "ATSTART",
7016 };
7017 #endif /* SQLITE_DEBUG */
7018
7019
7020 /* Each opcode is a "state" in the NFA */
7021 typedef unsigned short ReStateNumber;
7022
7023 /* Because this is an NFA and not a DFA, multiple states can be active at
@@ -7051,10 +6667,11 @@
7051 unsigned (*xNextChar)(ReInput*); /* Next character function */
7052 unsigned char zInit[12]; /* Initial text to match */
7053 int nInit; /* Number of bytes in zInit */
7054 unsigned nState; /* Number of entries in aOp[] and aArg[] */
7055 unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
 
7056 };
7057
7058 /* Add a state to the given state set if it is not already there */
7059 static void re_add_state(ReStateSet *pSet, int newState){
7060 unsigned i;
@@ -7265,18 +6882,19 @@
7265 return rc;
7266 }
7267
7268 /* Resize the opcode and argument arrays for an RE under construction.
7269 */
7270 static int re_resize(ReCompiled *p, int N){
7271 char *aOp;
7272 int *aArg;
 
7273 aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
7274 if( aOp==0 ) return 1;
7275 p->aOp = aOp;
7276 aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
7277 if( aArg==0 ) return 1;
7278 p->aArg = aArg;
7279 p->nAlloc = N;
7280 return 0;
7281 }
7282
@@ -7303,11 +6921,11 @@
7303 }
7304
7305 /* Make a copy of N opcodes starting at iStart onto the end of the RE
7306 ** under construction.
7307 */
7308 static void re_copy(ReCompiled *p, int iStart, int N){
7309 if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
7310 memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
7311 memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
7312 p->nState += N;
7313 }
@@ -7456,22 +7074,30 @@
7456 case '^': {
7457 re_append(p, RE_OP_ATSTART, 0);
7458 break;
7459 }
7460 case '{': {
7461 int m = 0, n = 0;
7462 int sz, j;
7463 if( iPrev<0 ) return "'{m,n}' without operand";
7464 while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
 
 
 
 
7465 n = m;
7466 if( c==',' ){
7467 p->sIn.i++;
7468 n = 0;
7469 while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
 
 
 
 
7470 }
7471 if( c!='}' ) return "unmatched '{'";
7472 if( n>0 && n<m ) return "n less than m in '{m,n}'";
7473 p->sIn.i++;
7474 sz = p->nState - iPrev;
7475 if( m==0 ){
7476 if( n==0 ) return "both m and n are zero in '{m,n}'";
7477 re_insert(p, iPrev, RE_OP_FORK, sz+1);
@@ -7483,11 +7109,11 @@
7483 for(j=m; j<n; j++){
7484 re_append(p, RE_OP_FORK, sz+1);
7485 re_copy(p, iPrev, sz);
7486 }
7487 if( n==0 && m>0 ){
7488 re_append(p, RE_OP_FORK, -sz);
7489 }
7490 break;
7491 }
7492 case '[': {
7493 unsigned int iFirst = p->nState;
@@ -7549,12 +7175,11 @@
7549
7550 /* Free and reclaim all the memory used by a previously compiled
7551 ** regular expression. Applications should invoke this routine once
7552 ** for every call to re_compile() to avoid memory leaks.
7553 */
7554 static void re_free(void *p){
7555 ReCompiled *pRe = (ReCompiled*)p;
7556 if( pRe ){
7557 sqlite3_free(pRe->aOp);
7558 sqlite3_free(pRe->aArg);
7559 sqlite3_free(pRe);
7560 }
@@ -7564,11 +7189,16 @@
7564 ** Compile a textual regular expression in zIn[] into a compiled regular
7565 ** expression suitable for us by re_match() and return a pointer to the
7566 ** compiled regular expression in *ppRe. Return NULL on success or an
7567 ** error message if something goes wrong.
7568 */
7569 static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
 
 
 
 
 
7570 ReCompiled *pRe;
7571 const char *zErr;
7572 int i, j;
7573
7574 *ppRe = 0;
@@ -7576,13 +7206,15 @@
7576 if( pRe==0 ){
7577 return "out of memory";
7578 }
7579 memset(pRe, 0, sizeof(*pRe));
7580 pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
 
7581 if( re_resize(pRe, 30) ){
 
7582 re_free(pRe);
7583 return "out of memory";
7584 }
7585 if( zIn[0]=='^' ){
7586 zIn++;
7587 }else{
7588 re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -7630,10 +7262,18 @@
7630 if( j>0 && pRe->zInit[j-1]==0 ) j--;
7631 pRe->nInit = j;
7632 }
7633 return pRe->zErr;
7634 }
 
 
 
 
 
 
 
 
7635
7636 /*
7637 ** Implementation of the regexp() SQL function. This function implements
7638 ** the build-in REGEXP operator. The first argument to the function is the
7639 ** pattern and the second argument is the string. So, the SQL statements:
@@ -7656,11 +7296,12 @@
7656 (void)argc; /* Unused */
7657 pRe = sqlite3_get_auxdata(context, 0);
7658 if( pRe==0 ){
7659 zPattern = (const char*)sqlite3_value_text(argv[0]);
7660 if( zPattern==0 ) return;
7661 zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
 
7662 if( zErr ){
7663 re_free(pRe);
7664 sqlite3_result_error(context, zErr, -1);
7665 return;
7666 }
@@ -7698,14 +7339,36 @@
7698 sqlite3_str *pStr;
7699 int i;
7700 int n;
7701 char *z;
7702 (void)argc;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7703
7704 zPattern = (const char*)sqlite3_value_text(argv[0]);
7705 if( zPattern==0 ) return;
7706 zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
 
7707 if( zErr ){
7708 re_free(pRe);
7709 sqlite3_result_error(context, zErr, -1);
7710 return;
7711 }
@@ -9284,18 +8947,20 @@
9284 if( idxNum & 1 ){
9285 pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
9286 if( pCur->nPrefix>0 ){
9287 pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
9288 if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
 
9289 }
9290 iArg = 1;
9291 }
9292 if( idxNum & 2 ){
9293 pCur->nLine = sqlite3_value_bytes(argv[iArg]);
9294 if( pCur->nLine>0 ){
9295 pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
9296 if( pCur->zLine==0 ) return SQLITE_NOMEM;
 
9297 }
9298 }
9299 if( pCur->zLine!=0 && pCur->zPrefix==0 ){
9300 int i = pCur->nLine;
9301 while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
@@ -9303,10 +8968,11 @@
9303 }
9304 pCur->nPrefix = pCur->nLine - i;
9305 if( pCur->nPrefix>0 ){
9306 pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
9307 if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
 
9308 }
9309 }
9310 pCur->iRowid = 0;
9311 pCur->ePhase = COMPLETION_FIRST_PHASE;
9312 return completionNext(pVtabCursor);
@@ -10216,11 +9882,17 @@
10216 "method," /* 6: Compression method (integer) */
10217 "z HIDDEN" /* 7: Name of zip file */
10218 ") WITHOUT ROWID;";
10219
10220 #define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
10221 #define ZIPFILE_BUFFER_SIZE (64*1024)
 
 
 
 
 
 
10222
10223
10224 /*
10225 ** Magic numbers used to read and write zip files.
10226 **
@@ -10773,10 +10445,11 @@
10773 pLFH->crc32 = zipfileRead32(aRead);
10774 pLFH->szCompressed = zipfileRead32(aRead);
10775 pLFH->szUncompressed = zipfileRead32(aRead);
10776 pLFH->nFile = zipfileRead16(aRead);
10777 pLFH->nExtra = zipfileRead16(aRead);
 
10778 }
10779 return rc;
10780 }
10781
10782
@@ -10898,10 +10571,19 @@
10898 || mUnixTime==zipfileMtime(pCds)
10899 || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
10900 /* || (mUnixTime % 2) */
10901 );
10902 }
 
 
 
 
 
 
 
 
 
10903
10904 /*
10905 ** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
10906 ** size) containing an entire zip archive image. Or, if aBlob is NULL,
10907 ** then pFile is a file-handle open on a zip file. In either case, this
@@ -10921,16 +10603,19 @@
10921 ZipfileEntry **ppEntry /* OUT: Pointer to new object */
10922 ){
10923 u8 *aRead;
10924 char **pzErr = &pTab->base.zErrMsg;
10925 int rc = SQLITE_OK;
10926 (void)nBlob;
10927
10928 if( aBlob==0 ){
10929 aRead = pTab->aBuffer;
10930 rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
10931 }else{
 
 
 
 
10932 aRead = (u8*)&aBlob[iOff];
10933 }
10934
10935 if( rc==SQLITE_OK ){
10936 sqlite3_int64 nAlloc;
@@ -10957,10 +10642,13 @@
10957 rc = zipfileReadData(
10958 pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
10959 );
10960 }else{
10961 aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
 
 
 
10962 }
10963 }
10964
10965 if( rc==SQLITE_OK ){
10966 u32 *pt = &pNew->mUnixTime;
@@ -10979,19 +10667,26 @@
10979 ZipfileLFH lfh;
10980 if( pFile ){
10981 rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
10982 }else{
10983 aRead = (u8*)&aBlob[pNew->cds.iOffset];
 
 
 
10984 }
10985
10986 if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
10987 if( rc==SQLITE_OK ){
10988 pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
10989 pNew->iDataOff += lfh.nFile + lfh.nExtra;
10990 if( aBlob && pNew->cds.szCompressed ){
10991 pNew->aData = &pNew->aExtra[nExtra];
10992 memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
 
 
 
 
10993 }
10994 }else{
10995 *pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
10996 (int)pNew->cds.iOffset
10997 );
@@ -11774,10 +11469,15 @@
11774
11775 if( rc==SQLITE_OK ){
11776 zPath = (const char*)sqlite3_value_text(apVal[2]);
11777 if( zPath==0 ) zPath = "";
11778 nPath = (int)strlen(zPath);
 
 
 
 
 
11779 mTime = zipfileGetTime(apVal[4]);
11780 }
11781
11782 if( rc==SQLITE_OK && bIsDir ){
11783 /* For a directory, check that the last character in the path is a
@@ -12135,10 +11835,17 @@
12135 if( zName==0 ){
12136 zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
12137 rc = SQLITE_ERROR;
12138 goto zipfile_step_out;
12139 }
 
 
 
 
 
 
 
12140
12141 /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
12142 ** deflate compression) or NULL (choose automatically). */
12143 if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
12144 iMethod = (int)sqlite3_value_int64(pMethod);
@@ -12477,10 +12184,11 @@
12477 return rc;
12478 }
12479
12480 /************************* End ../ext/misc/sqlar.c ********************/
12481 #endif
 
12482 /************************* Begin ../ext/expert/sqlite3expert.h ******************/
12483 /*
12484 ** 2017 April 07
12485 **
12486 ** The author disclaims copyright to this source code. In place of
@@ -14885,10 +14593,11 @@
14885 }
14886
14887 #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
14888
14889 /************************* End ../ext/expert/sqlite3expert.c ********************/
 
14890 /************************* Begin ../ext/intck/sqlite3intck.h ******************/
14891 /*
14892 ** 2024-02-08
14893 **
14894 ** The author disclaims copyright to this source code. In place of
@@ -21525,15 +21234,17 @@
21525 char **azFilter; /* Array of xFilter rejection GLOB patterns */
21526 sqlite3_session *p; /* The open session */
21527 };
21528 #endif
21529
 
21530 typedef struct ExpertInfo ExpertInfo;
21531 struct ExpertInfo {
21532 sqlite3expert *pExpert;
21533 int bVerbose;
21534 };
 
21535
21536 /* A single line in the EQP output */
21537 typedef struct EQPGraphRow EQPGraphRow;
21538 struct EQPGraphRow {
21539 int iEqpId; /* ID for this row */
@@ -21633,11 +21344,13 @@
21633 int *aiIndent; /* Array of indents used in MODE_Explain */
21634 int nIndent; /* Size of array aiIndent[] */
21635 int iIndent; /* Index of current op in aiIndent[] */
21636 char *zNonce; /* Nonce for temporary safe-mode escapes */
21637 EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
 
21638 ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
 
21639 #ifdef SQLITE_SHELL_FIDDLE
21640 struct {
21641 const char * zInput; /* Input string from wasm/JS proxy */
21642 const char * zPos; /* Cursor pos into zInput */
21643 const char * zDefaultDbName; /* Default name for db file */
@@ -21661,13 +21374,12 @@
21661 */
21662 #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
21663 #define SHELL_OPEN_NORMAL 1 /* Normal database file */
21664 #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
21665 #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
21666 #define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
21667 #define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
21668 #define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
21669
21670 /* Allowed values for ShellState.eTraceType
21671 */
21672 #define SHELL_TRACE_PLAIN 0 /* Show input SQL text */
21673 #define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */
@@ -22006,11 +21718,11 @@
22006 */
22007 static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
22008 int i;
22009 unsigned char *aBlob = (unsigned char*)pBlob;
22010
22011 char *zStr = sqlite3_malloc(nBlob*2 + 1);
22012 shell_check_oom(zStr);
22013
22014 for(i=0; i<nBlob; i++){
22015 static const char aHex[] = {
22016 '0', '1', '2', '3', '4', '5', '6', '7',
@@ -22027,11 +21739,11 @@
22027
22028 /*
22029 ** Output the given string as a quoted string using SQL quoting conventions:
22030 **
22031 ** (1) Single quotes (') within the string are doubled
22032 ** (2) The whle string is enclosed in '...'
22033 ** (3) Control characters other than \n, \t, and \r\n are escaped
22034 ** using \u00XX notation and if such substitutions occur,
22035 ** the whole string is enclosed in unistr('...') instead of '...'.
22036 **
22037 ** Step (3) is omitted if the control-character escape mode is OFF.
@@ -22273,11 +21985,11 @@
22273 **
22274 ** Escaping is needed if the string contains any control characters
22275 ** other than \t, \n, and \r\n
22276 **
22277 ** If no escaping is needed (the common case) then set *ppFree to NULL
22278 ** and return the original string. If escapingn is needed, write the
22279 ** escaped string into memory obtained from sqlite3_malloc64() or the
22280 ** equivalent, and return the new string and set *ppFree to the new string
22281 ** as well.
22282 **
22283 ** The caller is responsible for freeing *ppFree if it is non-NULL in order
@@ -23278,32 +22990,21 @@
23278 ** Set the destination table field of the ShellState structure to
23279 ** the name of the table given. Escape any quote characters in the
23280 ** table name.
23281 */
23282 static void set_table_name(ShellState *p, const char *zName){
23283 int i, n;
23284 char cQuote;
23285 char *z;
23286
23287 if( p->zDestTable ){
23288 free(p->zDestTable);
23289 p->zDestTable = 0;
23290 }
23291 if( zName==0 ) return;
23292 cQuote = quoteChar(zName);
23293 n = strlen30(zName);
23294 if( cQuote ) n += n+2;
23295 z = p->zDestTable = malloc( n+1 );
23296 shell_check_oom(z);
23297 n = 0;
23298 if( cQuote ) z[n++] = cQuote;
23299 for(i=0; zName[i]; i++){
23300 z[n++] = zName[i];
23301 if( zName[i]==cQuote ) z[n++] = cQuote;
23302 }
23303 if( cQuote ) z[n++] = cQuote;
23304 z[n] = 0;
23305 }
23306
23307 /*
23308 ** Maybe construct two lines of text that point out the position of a
23309 ** syntax error. Return a pointer to the text, in memory obtained from
@@ -23496,12 +23197,12 @@
23496 static int display_stats(
23497 sqlite3 *db, /* Database to query */
23498 ShellState *pArg, /* Pointer to ShellState */
23499 int bReset /* True to reset the stats */
23500 ){
23501 int iCur;
23502 int iHiwtr;
23503 FILE *out;
23504 if( pArg==0 || pArg->out==0 ) return 0;
23505 out = pArg->out;
23506
23507 if( pArg->pStmt && pArg->statsOn==2 ){
@@ -23586,18 +23287,25 @@
23586 "Page cache hits: %d\n", iCur);
23587 iHiwtr = iCur = -1;
23588 sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
23589 sqlite3_fprintf(out,
23590 "Page cache misses: %d\n", iCur);
 
 
 
23591 iHiwtr = iCur = -1;
23592 sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
23593 sqlite3_fprintf(out,
23594 "Page cache writes: %d\n", iCur);
23595 iHiwtr = iCur = -1;
23596 sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
23597 sqlite3_fprintf(out,
23598 "Page cache spills: %d\n", iCur);
 
 
 
 
23599 iHiwtr = iCur = -1;
23600 sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
23601 sqlite3_fprintf(out,
23602 "Schema Heap Usage: %d bytes\n", iCur);
23603 iHiwtr = iCur = -1;
@@ -23999,10 +23707,40 @@
23999 char *zBuf = sqlite3_malloc64( szVar-5 );
24000 if( zBuf ){
24001 memcpy(zBuf, &zVar[6], szVar-5);
24002 sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
24003 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24004 }else{
24005 sqlite3_bind_null(pStmt, i);
24006 }
24007 sqlite3_reset(pQ);
24008 }
@@ -24581,11 +24319,11 @@
24581 }
24582 }
24583 }
24584 }
24585
24586 #ifndef SQLITE_OMIT_VIRTUALTABLE
24587 /*
24588 ** This function is called to process SQL if the previous shell command
24589 ** was ".expert". It passes the SQL in the second argument directly to
24590 ** the sqlite3expert object.
24591 **
@@ -24713,11 +24451,11 @@
24713 }
24714 sqlite3_free(zErr);
24715
24716 return rc;
24717 }
24718 #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
24719
24720 /*
24721 ** Execute a statement or set of statements. Print
24722 ** any result rows/columns depending on the current mode
24723 ** set via the supplied callback.
@@ -24739,11 +24477,11 @@
24739
24740 if( pzErrMsg ){
24741 *pzErrMsg = NULL;
24742 }
24743
24744 #ifndef SQLITE_OMIT_VIRTUALTABLE
24745 if( pArg->expert.pExpert ){
24746 rc = expertHandleSQL(pArg, zSql, pzErrMsg);
24747 return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
24748 }
24749 #endif
@@ -24901,11 +24639,11 @@
24901 static char **tableColumnList(ShellState *p, const char *zTab){
24902 char **azCol = 0;
24903 sqlite3_stmt *pStmt;
24904 char *zSql;
24905 int nCol = 0;
24906 int nAlloc = 0;
24907 int nPK = 0; /* Number of PRIMARY KEY columns seen */
24908 int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
24909 int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid);
24910 int rc;
24911
@@ -24915,11 +24653,11 @@
24915 sqlite3_free(zSql);
24916 if( rc ) return 0;
24917 while( sqlite3_step(pStmt)==SQLITE_ROW ){
24918 if( nCol>=nAlloc-2 ){
24919 nAlloc = nAlloc*2 + nCol + 10;
24920 azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
24921 shell_check_oom(azCol);
24922 }
24923 azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
24924 shell_check_oom(azCol[nCol]);
24925 if( sqlite3_column_int(pStmt, 5) ){
@@ -25106,17 +24844,17 @@
25106 appendText(&sSelect, " FROM ", 0);
25107 appendText(&sSelect, zTable, quoteChar(zTable));
25108
25109 savedDestTable = p->zDestTable;
25110 savedMode = p->mode;
25111 p->zDestTable = sTable.z;
25112 p->mode = p->cMode = MODE_Insert;
25113 rc = shell_exec(p, sSelect.z, 0);
25114 if( (rc&0xff)==SQLITE_CORRUPT ){
25115 sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
25116 toggleSelectOrder(p->db);
25117 shell_exec(p, sSelect.z, 0);
25118 toggleSelectOrder(p->db);
25119 }
25120 p->zDestTable = savedDestTable;
25121 p->mode = savedMode;
25122 freeText(&sTable);
@@ -25245,11 +24983,13 @@
25245 " --bom Put a UTF8 byte-order mark on intermediate file",
25246 #endif
25247 #ifndef SQLITE_SHELL_FIDDLE
25248 ".exit ?CODE? Exit this program with return-code CODE",
25249 #endif
 
25250 ".expert EXPERIMENTAL. Suggest indexes for queries",
 
25251 ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
25252 ".filectrl CMD ... Run various sqlite3_file_control() operations",
25253 " --schema SCHEMA Use SCHEMA instead of \"main\"",
25254 " --help Show CMD details",
25255 ".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
@@ -25270,11 +25010,11 @@
25270 " from the \".mode\" output mode",
25271 " * If FILE begins with \"|\" then it is a command that generates the",
25272 " input text.",
25273 #endif
25274 #ifndef SQLITE_OMIT_TEST_CONTROL
25275 ",imposter INDEX TABLE Create imposter table TABLE on index INDEX",
25276 #endif
25277 ".indexes ?TABLE? Show names of indexes",
25278 " If TABLE is specified, only show indexes for",
25279 " tables matching TABLE using the LIKE operator.",
25280 ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db",
@@ -25337,11 +25077,17 @@
25337 " Options:",
25338 " --append Use appendvfs to append database to the end of FILE",
25339 #endif
25340 #ifndef SQLITE_OMIT_DESERIALIZE
25341 " --deserialize Load into memory using sqlite3_deserialize()",
 
 
 
25342 " --hexdb Load the output of \"dbtotxt\" as an in-memory db",
 
 
 
25343 " --maxsize N Maximum size for --hexdb or --deserialized database",
25344 #endif
25345 " --new Initialize FILE to an empty database",
25346 " --nofollow Do not follow symbolic links",
25347 " --readonly Open FILE readonly",
@@ -25726,14 +25472,14 @@
25726 ** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
25727 */
25728 static unsigned char *readHexDb(ShellState *p, int *pnData){
25729 unsigned char *a = 0;
25730 int nLine;
25731 int n = 0;
 
25732 int pgsz = 0;
25733 int iOffset = 0;
25734 int j, k;
25735 int rc;
25736 FILE *in;
25737 const char *zDbFilename = p->pAuxDb->zDbFilename;
25738 unsigned int x[16];
25739 char zLine[1000];
@@ -25753,20 +25499,21 @@
25753 nLine++;
25754 if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
25755 rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
25756 if( rc!=2 ) goto readHexDb_error;
25757 if( n<0 ) goto readHexDb_error;
25758 if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error;
25759 n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
25760 a = sqlite3_malloc( n ? n : 1 );
25761 shell_check_oom(a);
25762 memset(a, 0, n);
25763 if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
25764 sqlite3_fputs("invalid pagesize\n", stderr);
25765 goto readHexDb_error;
25766 }
 
 
 
 
25767 for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
 
 
25768 rc = sscanf(zLine, "| page %d offset %d", &j, &k);
25769 if( rc==2 ){
25770 iOffset = k;
25771 continue;
25772 }
@@ -25775,18 +25522,18 @@
25775 }
25776 rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
25777 &j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
25778 &x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
25779 if( rc==17 ){
25780 k = iOffset+j;
25781 if( k+16<=n && k>=0 ){
25782 int ii;
25783 for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
25784 }
25785 }
25786 }
25787 *pnData = n;
25788 if( in!=p->in ){
25789 fclose(in);
25790 }else{
25791 p->lineno = nLine;
25792 }
@@ -25849,11 +25596,11 @@
25849 p->pLog = pSavedLog;
25850
25851 if( zFake ){
25852 sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
25853 -1, sqlite3_free);
25854 free(zFake);
25855 }
25856 }
25857
25858 /* Flags for open_db().
25859 **
@@ -25881,14 +25628,17 @@
25881 }else{
25882 p->openMode = (u8)deduceDatabaseType(zDbFilename,
25883 (openFlags & OPEN_DB_ZIPFILE)!=0);
25884 }
25885 }
 
 
 
 
25886 switch( p->openMode ){
25887 case SHELL_OPEN_APPENDVFS: {
25888 sqlite3_open_v2(zDbFilename, &p->db,
25889 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
25890 break;
25891 }
25892 case SHELL_OPEN_HEXDB:
25893 case SHELL_OPEN_DESERIALIZE: {
25894 sqlite3_open(0, &p->db);
@@ -25896,19 +25646,13 @@
25896 }
25897 case SHELL_OPEN_ZIPFILE: {
25898 sqlite3_open(":memory:", &p->db);
25899 break;
25900 }
25901 case SHELL_OPEN_READONLY: {
25902 sqlite3_open_v2(zDbFilename, &p->db,
25903 SQLITE_OPEN_READONLY|p->openFlags, 0);
25904 break;
25905 }
25906 case SHELL_OPEN_UNSPEC:
25907 case SHELL_OPEN_NORMAL: {
25908 sqlite3_open_v2(zDbFilename, &p->db,
25909 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
25910 break;
25911 }
25912 }
25913 if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
25914 sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
@@ -25944,11 +25688,10 @@
25944 sqlite3_sha_init(p->db, 0, 0);
25945 sqlite3_shathree_init(p->db, 0, 0);
25946 sqlite3_uint_init(p->db, 0, 0);
25947 sqlite3_stmtrand_init(p->db, 0, 0);
25948 sqlite3_decimal_init(p->db, 0, 0);
25949 sqlite3_percentile_init(p->db, 0, 0);
25950 sqlite3_base64_init(p->db, 0, 0);
25951 sqlite3_base85_init(p->db, 0, 0);
25952 sqlite3_regexp_init(p->db, 0, 0);
25953 sqlite3_ieee_init(p->db, 0, 0);
25954 sqlite3_series_init(p->db, 0, 0);
@@ -26043,13 +25786,15 @@
26043 }
26044 }
26045 #endif
26046 }
26047 if( p->db!=0 ){
 
26048 if( p->bSafeModePersist ){
26049 sqlite3_set_authorizer(p->db, safeModeAuth, p);
26050 }
 
26051 sqlite3_db_config(
26052 p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
26053 );
26054 }
26055 }
@@ -26358,12 +26103,12 @@
26358 struct ImportCtx {
26359 const char *zFile; /* Name of the input file */
26360 FILE *in; /* Read the CSV text from this input stream */
26361 int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
26362 char *z; /* Accumulated text for a field */
26363 int n; /* Number of bytes in z */
26364 int nAlloc; /* Space allocated for z[] */
26365 int nLine; /* Current line number */
26366 int nRow; /* Number of rows imported */
26367 int nErr; /* Number of errors encountered */
26368 int bNotFirst; /* True if one or more bytes already read */
26369 int cTerm; /* Character that terminated the most recent field */
@@ -26829,14 +26574,17 @@
26829 #if SQLITE_SHELL_HAVE_RECOVER
26830 /*
26831 ** Convert a 2-byte or 4-byte big-endian integer into a native integer
26832 */
26833 static unsigned int get2byteInt(unsigned char *a){
26834 return (a[0]<<8) + a[1];
26835 }
26836 static unsigned int get4byteInt(unsigned char *a){
26837 return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
 
 
 
26838 }
26839
26840 /*
26841 ** Implementation of the ".dbinfo" command.
26842 **
@@ -26969,11 +26717,11 @@
26969 if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
26970 nPage = sqlite3_column_int64(pStmt, 0);
26971 sqlite3_finalize(pStmt);
26972 pStmt = 0;
26973 if( nPage<1 ) goto dbtotxt_error;
26974 rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
26975 if( rc ) goto dbtotxt_error;
26976 if( sqlite3_step(pStmt)!=SQLITE_ROW ){
26977 zTail = "unk.db";
26978 }else{
26979 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
@@ -26980,10 +26728,15 @@
26980 if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
26981 zTail = strrchr(zFilename, '/');
26982 #if defined(_WIN32)
26983 if( zTail==0 ) zTail = strrchr(zFilename, '\\');
26984 #endif
 
 
 
 
 
26985 }
26986 zName = strdup(zTail);
26987 shell_check_oom(zName);
26988 sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
26989 nPage*pgSz, pgSz, zName);
@@ -27148,10 +26901,47 @@
27148 if( zStr[0]!='-' ) return 0;
27149 zStr++;
27150 if( zStr[0]=='-' ) zStr++;
27151 return cli_strcmp(zStr, zOpt)==0;
27152 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27153
27154 /*
27155 ** Delete a file.
27156 */
27157 int shellDeleteFile(const char *zFilename){
@@ -28704,11 +28494,11 @@
28704 int nArg = 0;
28705 int n, c;
28706 int rc = 0;
28707 char *azArg[52];
28708
28709 #ifndef SQLITE_OMIT_VIRTUALTABLE
28710 if( p->expert.pExpert ){
28711 expertFinish(p, 1, 0);
28712 }
28713 #endif
28714
@@ -29005,11 +28795,11 @@
29005 }else{
29006 while( sqlite3_step(pStmt)==SQLITE_ROW ){
29007 const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
29008 const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
29009 if( zSchema==0 || zFile==0 ) continue;
29010 azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*));
29011 shell_check_oom(azName);
29012 azName[nName*2] = strdup(zSchema);
29013 azName[nName*2+1] = strdup(zFile);
29014 nName++;
29015 }
@@ -29208,10 +28998,11 @@
29208 rc = 1;
29209 }
29210 }else
29211
29212 if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
 
29213 rc = shell_dbtotxt_command(p, nArg, azArg);
29214 }else
29215
29216 if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
29217 if( nArg==2 ){
@@ -29273,11 +29064,11 @@
29273 if( p->mode==MODE_Explain ) p->mode = p->normalMode;
29274 p->autoExplain = 1;
29275 }
29276 }else
29277
29278 #ifndef SQLITE_OMIT_VIRTUALTABLE
29279 if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
29280 if( p->bSafeMode ){
29281 sqlite3_fprintf(stderr,
29282 "Cannot run experimental commands such as \"%s\" in safe mode\n",
29283 azArg[0]);
@@ -29840,16 +29631,10 @@
29840 sqlite3_stmt *pStmt;
29841 int tnum = 0;
29842 int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
29843 int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
29844 int i;
29845 if( !ShellHasFlag(p,SHFLG_TestingMode) ){
29846 sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n",
29847 "imposter");
29848 rc = 1;
29849 goto meta_command_exit;
29850 }
29851 if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
29852 eputz("Usage: .imposter INDEX IMPOSTER\n"
29853 " .imposter off\n");
29854 /* Also allowed, but not documented:
29855 **
@@ -29917,22 +29702,19 @@
29917 if( lenPK==0 ) lenPK = 100000;
29918 zSql = sqlite3_mprintf(
29919 "CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
29920 azArg[2], zCollist, lenPK, zCollist);
29921 sqlite3_free(zCollist);
29922 rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
29923 if( rc==SQLITE_OK ){
29924 rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
29925 sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
29926 if( rc ){
29927 sqlite3_fprintf(stderr,
29928 "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
29929 }else{
29930 sqlite3_fprintf(stdout, "%s;\n", zSql);
29931 sqlite3_fprintf(stdout,
29932 "WARNING: writing to an imposter table will corrupt"
29933 " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index");
29934 }
29935 }else{
29936 sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
29937 rc = 1;
29938 }
@@ -30282,10 +30064,11 @@
30282 const char *zFN = 0; /* Pointer to constant filename */
30283 char *zNewFilename = 0; /* Name of the database file to open */
30284 int iName = 1; /* Index in azArg[] of the filename */
30285 int newFlag = 0; /* True to delete file before opening */
30286 int openMode = SHELL_OPEN_UNSPEC;
 
30287
30288 /* Check for command-line arguments */
30289 for(iName=1; iName<nArg; iName++){
30290 const char *z = azArg[iName];
30291 #ifndef SQLITE_SHELL_FIDDLE
@@ -30296,13 +30079,18 @@
30296 openMode = SHELL_OPEN_ZIPFILE;
30297 #endif
30298 }else if( optionMatch(z, "append") ){
30299 openMode = SHELL_OPEN_APPENDVFS;
30300 }else if( optionMatch(z, "readonly") ){
30301 openMode = SHELL_OPEN_READONLY;
 
 
 
 
 
30302 }else if( optionMatch(z, "nofollow") ){
30303 p->openFlags |= SQLITE_OPEN_NOFOLLOW;
30304 #ifndef SQLITE_OMIT_DESERIALIZE
30305 }else if( optionMatch(z, "deserialize") ){
30306 openMode = SHELL_OPEN_DESERIALIZE;
30307 }else if( optionMatch(z, "hexdb") ){
30308 openMode = SHELL_OPEN_HEXDB;
@@ -30330,16 +30118,25 @@
30330 p->db = 0;
30331 p->pAuxDb->zDbFilename = 0;
30332 sqlite3_free(p->pAuxDb->zFreeOnClose);
30333 p->pAuxDb->zFreeOnClose = 0;
30334 p->openMode = openMode;
30335 p->openFlags = 0;
30336 p->szMax = 0;
30337
30338 /* If a filename is specified, try to open it first */
30339 if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
30340 if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN);
 
 
 
 
 
 
 
 
 
30341 #ifndef SQLITE_SHELL_FIDDLE
30342 if( p->bSafeMode
30343 && p->openMode!=SHELL_OPEN_HEXDB
30344 && zFN
30345 && cli_strcmp(zFN,":memory:")!=0
@@ -30938,13 +30735,13 @@
30938 appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
30939 }
30940 appendText(&sSelect, "sql IS NOT NULL"
30941 " ORDER BY snum, rowid", 0);
30942 if( bDebug ){
30943 sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z);
30944 }else{
30945 rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
30946 }
30947 freeText(&sSelect);
30948 }
30949 if( zErrMsg ){
30950 shellEmitError(zErrMsg);
@@ -31072,19 +30869,20 @@
31072
31073 /* .session filter GLOB ....
31074 ** Set a list of GLOB patterns of table names to be excluded.
31075 */
31076 if( cli_strcmp(azCmd[0], "filter")==0 ){
31077 int ii, nByte;
 
31078 if( nCmd<2 ) goto session_syntax_error;
31079 if( pAuxDb->nSession ){
31080 for(ii=0; ii<pSession->nFilter; ii++){
31081 sqlite3_free(pSession->azFilter[ii]);
31082 }
31083 sqlite3_free(pSession->azFilter);
31084 nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
31085 pSession->azFilter = sqlite3_malloc( nByte );
31086 shell_check_oom( pSession->azFilter );
31087 for(ii=1; ii<nCmd; ii++){
31088 char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
31089 shell_check_oom(x);
31090 }
@@ -31264,26 +31062,26 @@
31264 sqlite3_fprintf(p->out, "%s\n", zSql);
31265 }else
31266 if( cli_strcmp(zOp,"run")==0 ){
31267 char *zErrMsg = 0;
31268 str.n = 0;
31269 str.z[0] = 0;
31270 rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
31271 nTest++;
31272 if( bVerbose ){
31273 sqlite3_fprintf(p->out, "Result: %s\n", str.z);
31274 }
31275 if( rc || zErrMsg ){
31276 nErr++;
31277 rc = 1;
31278 sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
31279 sqlite3_free(zErrMsg);
31280 }else if( cli_strcmp(zAns,str.z)!=0 ){
31281 nErr++;
31282 rc = 1;
31283 sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns);
31284 sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z);
31285 }
31286 }
31287 else{
31288 sqlite3_fprintf(stderr,
31289 "Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
@@ -31395,11 +31193,11 @@
31395 appendText(&sQuery, "SELECT * FROM ", 0);
31396 appendText(&sQuery, zTab, 0);
31397 appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
31398 }
31399 appendText(&sSql, zSep, 0);
31400 appendText(&sSql, sQuery.z, '\'');
31401 sQuery.n = 0;
31402 appendText(&sSql, ",", 0);
31403 appendText(&sSql, zTab, '\'');
31404 zSep = "),(";
31405 }
@@ -31407,17 +31205,17 @@
31407 if( bSeparate ){
31408 zSql = sqlite3_mprintf(
31409 "%s))"
31410 " SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label"
31411 " FROM [sha3sum$query]",
31412 sSql.z, iSize);
31413 }else{
31414 zSql = sqlite3_mprintf(
31415 "%s))"
31416 " SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash"
31417 " FROM [sha3sum$query]",
31418 sSql.z, iSize);
31419 }
31420 shell_check_oom(zSql);
31421 freeText(&sQuery);
31422 freeText(&sSql);
31423 if( bDebug ){
@@ -31613,11 +31411,11 @@
31613 goto meta_command_exit;
31614 }
31615 for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
31616 const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
31617 if( zDbName==0 ) continue;
31618 if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0);
31619 if( sqlite3_stricmp(zDbName, "main")==0 ){
31620 appendText(&s, "SELECT name FROM ", 0);
31621 }else{
31622 appendText(&s, "SELECT ", 0);
31623 appendText(&s, zDbName, '\'');
@@ -31635,11 +31433,11 @@
31635 }
31636 }
31637 rc = sqlite3_finalize(pStmt);
31638 if( rc==SQLITE_OK ){
31639 appendText(&s, " ORDER BY 1", 0);
31640 rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
31641 }
31642 freeText(&s);
31643 if( rc ) return shellDatabaseError(p->db);
31644
31645 /* Run the SQL statement prepared by the above block. Store the results
@@ -31652,14 +31450,14 @@
31652 sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
31653 }
31654 while( sqlite3_step(pStmt)==SQLITE_ROW ){
31655 if( nRow>=nAlloc ){
31656 char **azNew;
31657 int n2 = nAlloc*2 + 10;
31658 azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
31659 shell_check_oom(azNew);
31660 nAlloc = n2;
31661 azResult = azNew;
31662 }
31663 azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
31664 shell_check_oom(azResult[nRow]);
31665 nRow++;
@@ -31818,11 +31616,11 @@
31818 { 0x00000020, 1, "CoverIdxScan" },
31819 { 0x00000040, 1, "OrderByIdxJoin" },
31820 { 0x00000080, 1, "Transitive" },
31821 { 0x00000100, 1, "OmitNoopJoin" },
31822 { 0x00000200, 1, "CountOfView" },
31823 { 0x00000400, 1, "CurosrHints" },
31824 { 0x00000800, 1, "Stat4" },
31825 { 0x00001000, 1, "PushDown" },
31826 { 0x00002000, 1, "SimplifyJoin" },
31827 { 0x00004000, 1, "SkipScan" },
31828 { 0x00008000, 1, "PropagateConst" },
@@ -32365,11 +32163,14 @@
32365 p->nWidth = nArg-1;
32366 p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
32367 if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
32368 if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
32369 for(j=1; j<nArg; j++){
32370 p->colWidth[j-1] = (int)integerValue(azArg[j]);
 
 
 
32371 }
32372 }else
32373
32374 {
32375 sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: "
@@ -32888,76 +32689,95 @@
32888
32889 return home_dir;
32890 }
32891
32892 /*
32893 ** On non-Windows platforms, look for $XDG_CONFIG_HOME.
32894 ** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
32895 ** the path to it. If there is no $(XDG_CONFIG_HOME) then
32896 ** look for $(HOME)/.config/sqlite3/sqliterc and if found
32897 ** return that. If none of these are found, return 0.
 
 
 
 
 
 
 
 
 
 
 
 
 
32898 **
32899 ** The string returned is obtained from sqlite3_malloc() and
32900 ** should be freed by the caller.
32901 */
32902 static char *find_xdg_config(void){
 
32903 #if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
32904 || defined(__RTP__) || defined(_WRS_KERNEL)
32905 return 0;
32906 #else
32907 char *zConfig = 0;
32908 const char *zXdgHome;
32909
32910 zXdgHome = getenv("XDG_CONFIG_HOME");
32911 if( zXdgHome==0 ){
32912 const char *zHome = getenv("HOME");
32913 if( zHome==0 ) return 0;
32914 zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
32915 }else{
32916 zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
32917 }
32918 shell_check_oom(zConfig);
32919 if( access(zConfig,0)!=0 ){
32920 sqlite3_free(zConfig);
32921 zConfig = 0;
32922 }
32923 return zConfig;
 
 
 
 
32924 #endif
32925 }
32926
32927 /*
32928 ** Read input from the file given by sqliterc_override. Or if that
32929 ** parameter is NULL, take input from the first of find_xdg_config()
32930 ** or ~/.sqliterc which is found.
32931 **
32932 ** Returns the number of errors.
 
 
 
32933 */
32934 static void process_sqliterc(
32935 ShellState *p, /* Configuration data */
32936 const char *sqliterc_override /* Name of config file. NULL to use default */
32937 ){
32938 char *home_dir = NULL;
32939 const char *sqliterc = sqliterc_override;
32940 char *zBuf = 0;
32941 FILE *inSaved = p->in;
32942 int savedLineno = p->lineno;
32943
32944 if( sqliterc == NULL ){
32945 sqliterc = zBuf = find_xdg_config();
 
 
32946 }
32947 if( sqliterc == NULL ){
32948 home_dir = find_home_dir(0);
32949 if( home_dir==0 ){
32950 eputz("-- warning: cannot find home directory;"
32951 " cannot read ~/.sqliterc\n");
32952 return;
32953 }
32954 zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
32955 shell_check_oom(zBuf);
32956 sqliterc = zBuf;
32957 }
32958 p->in = sqlite3_fopen(sqliterc,"rb");
32959 if( p->in ){
32960 if( stdin_is_interactive ){
32961 sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
32962 }
32963 if( process_input(p) && bail_on_error ) exit(1);
@@ -32966,11 +32786,13 @@
32966 sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
32967 if( bail_on_error ) exit(1);
32968 }
32969 p->in = inSaved;
32970 p->lineno = savedLineno;
32971 sqlite3_free(zBuf);
 
 
32972 }
32973
32974 /*
32975 ** Show available command line options
32976 */
@@ -32997,10 +32819,11 @@
32997 #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
32998 " -heap SIZE Size of heap for memsys3 or memsys5\n"
32999 #endif
33000 " -help show this message\n"
33001 " -html set output mode to HTML\n"
 
33002 " -interactive force interactive I/O\n"
33003 " -json set output mode to 'json'\n"
33004 " -line set output mode to 'line'\n"
33005 " -list set output mode to 'list'\n"
33006 " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
@@ -33341,16 +33164,24 @@
33341 (void)cmdline_option_value(argc, argv, ++i);
33342 #endif
33343 }else if( cli_strcmp(z,"-pagecache")==0 ){
33344 sqlite3_int64 n, sz;
33345 sz = integerValue(cmdline_option_value(argc,argv,++i));
33346 if( sz>70000 ) sz = 70000;
33347 if( sz<0 ) sz = 0;
33348 n = integerValue(cmdline_option_value(argc,argv,++i));
33349 if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
33350 n = 0xffffffffffffLL/sz;
33351 }
 
 
 
 
 
 
 
 
33352 verify_uninitialized();
33353 sqlite3_config(SQLITE_CONFIG_PAGECACHE,
33354 (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
33355 data.shellFlgs |= SHFLG_Pagecache;
33356 }else if( cli_strcmp(z,"-lookaside")==0 ){
@@ -33359,11 +33190,11 @@
33359 if( sz<0 ) sz = 0;
33360 n = (int)integerValue(cmdline_option_value(argc,argv,++i));
33361 if( n<0 ) n = 0;
33362 verify_uninitialized();
33363 sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
33364 if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
33365 }else if( cli_strcmp(z,"-threadsafe")==0 ){
33366 int n;
33367 n = (int)integerValue(cmdline_option_value(argc,argv,++i));
33368 verify_uninitialized();
33369 switch( n ){
@@ -33401,13 +33232,19 @@
33401 data.openMode = SHELL_OPEN_DESERIALIZE;
33402 }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
33403 data.szMax = integerValue(argv[++i]);
33404 #endif
33405 }else if( cli_strcmp(z,"-readonly")==0 ){
33406 data.openMode = SHELL_OPEN_READONLY;
 
33407 }else if( cli_strcmp(z,"-nofollow")==0 ){
33408 data.openFlags = SQLITE_OPEN_NOFOLLOW;
 
 
 
 
 
33409 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
33410 }else if( cli_strncmp(z, "-A",2)==0 ){
33411 /* All remaining command-line arguments are passed to the ".archive"
33412 ** command, so ignore them */
33413 break;
@@ -33557,13 +33394,19 @@
33557 data.openMode = SHELL_OPEN_DESERIALIZE;
33558 }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
33559 data.szMax = integerValue(argv[++i]);
33560 #endif
33561 }else if( cli_strcmp(z,"-readonly")==0 ){
33562 data.openMode = SHELL_OPEN_READONLY;
 
33563 }else if( cli_strcmp(z,"-nofollow")==0 ){
33564 data.openFlags |= SQLITE_OPEN_NOFOLLOW;
 
 
 
 
 
33565 }else if( cli_strcmp(z,"-ascii")==0 ){
33566 data.mode = MODE_Ascii;
33567 sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit);
33568 sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record);
33569 }else if( cli_strcmp(z,"-tabs")==0 ){
@@ -33734,11 +33577,10 @@
33734 /* Run commands received from standard input
33735 */
33736 if( stdin_is_interactive ){
33737 char *zHome;
33738 char *zHistory;
33739 int nHistory;
33740 sqlite3_fprintf(stdout,
33741 "SQLite version %s %.19s\n" /*extra-version-info*/
33742 "Enter \".help\" for usage hints.\n",
33743 sqlite3_libversion(), sqlite3_sourceid());
33744 if( warnInmemoryDb ){
@@ -33747,15 +33589,19 @@
33747 sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
33748 " persistent database.\n");
33749 }
33750 zHistory = getenv("SQLITE_HISTORY");
33751 if( zHistory ){
33752 zHistory = strdup(zHistory);
33753 }else if( (zHome = find_home_dir(0))!=0 ){
33754 nHistory = strlen30(zHome) + 20;
33755 if( (zHistory = malloc(nHistory))!=0 ){
33756 sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
 
 
 
 
33757 }
33758 }
33759 if( zHistory ){ shell_read_history(zHistory); }
33760 #if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
33761 rl_attempted_completion_function = readline_completion;
@@ -33767,21 +33613,21 @@
33767 data.in = 0;
33768 rc = process_input(&data);
33769 if( zHistory ){
33770 shell_stifle_history(2000);
33771 shell_write_history(zHistory);
33772 free(zHistory);
33773 }
33774 }else{
33775 data.in = stdin;
33776 rc = process_input(&data);
33777 }
33778 }
33779 #ifndef SQLITE_SHELL_FIDDLE
33780 /* In WASM mode we have to leave the db state in place so that
33781 ** client code can "push" SQL into it after this call returns. */
33782 #ifndef SQLITE_OMIT_VIRTUALTABLE
33783 if( data.expert.pExpert ){
33784 expertFinish(&data, 1, 0);
33785 }
33786 #endif
33787 shell_main_exit:
33788
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -122,10 +122,13 @@
122 typedef sqlite3_int64 i64;
123 typedef sqlite3_uint64 u64;
124 typedef unsigned char u8;
125 #include <ctype.h>
126 #include <stdarg.h>
127 #ifndef _WIN32
128 # include <sys/time.h>
129 #endif
130
131 #if !defined(_WIN32) && !defined(WIN32)
132 # include <signal.h>
133 # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
134 # include <pwd.h>
@@ -646,12 +649,23 @@
649 if( a==0 ) a = "";
650 if( b==0 ) b = "";
651 return strncmp(a,b,n);
652 }
653
654 /* Return the current wall-clock time in microseconds since the
655 ** Unix epoch (1970-01-01T00:00:00Z)
656 */
657 static sqlite3_int64 timeOfDay(void){
658 #if defined(_WIN64)
659 sqlite3_uint64 t;
660 FILETIME tm;
661 GetSystemTimePreciseAsFileTime(&tm);
662 t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
663 t += 116444736000000000LL;
664 t /= 10;
665 return t;
666 #elif defined(_WIN32)
667 static sqlite3_vfs *clockVfs = 0;
668 sqlite3_int64 t;
669 if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
670 if( clockVfs==0 ) return 0; /* Never actually happens */
671 if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
@@ -659,11 +673,16 @@
673 }else{
674 double r;
675 clockVfs->xCurrentTime(clockVfs, &r);
676 t = (sqlite3_int64)(r*86400000.0);
677 }
678 return t*1000;
679 #else
680 struct timeval sNow;
681 (void)gettimeofday(&sNow,0);
682 return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
683 #endif
684 }
685
686 #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
687 #include <sys/time.h>
688 #include <sys/resource.h>
@@ -704,12 +723,12 @@
723 static void endTimer(FILE *out){
724 if( enableTimer ){
725 sqlite3_int64 iEnd = timeOfDay();
726 struct rusage sEnd;
727 getrusage(RUSAGE_SELF, &sEnd);
728 sqlite3_fprintf(out, "Run Time: real %.6f user %.6f sys %.6f\n",
729 (iEnd - iBegin)*0.000001,
730 timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
731 timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
732 }
733 }
734
@@ -783,14 +802,23 @@
802 static void endTimer(FILE *out){
803 if( enableTimer && getProcessTimesAddr){
804 FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
805 sqlite3_int64 ftWallEnd = timeOfDay();
806 getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
807 #ifdef _WIN64
808 /* microsecond precision on 64-bit windows */
809 sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
810 (ftWallEnd - ftWallBegin)*0.000001,
811 timeDiff(&ftUserBegin, &ftUserEnd),
812 timeDiff(&ftKernelBegin, &ftKernelEnd));
813 #else
814 /* millisecond precisino on 32-bit windows */
815 sqlite3_fprintf(out, "Run Time: real %.3f user %.3f sys %.3f\n",
816 (ftWallEnd - ftWallBegin)*0.000001,
817 timeDiff(&ftUserBegin, &ftUserEnd),
818 timeDiff(&ftKernelBegin, &ftKernelEnd));
819 #endif
820 }
821 }
822
823 #define BEGIN_TIMER beginTimer()
824 #define END_TIMER(X) endTimer(X)
@@ -1126,11 +1154,11 @@
1154 }
1155 if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80
1156 && (z[3] & 0xc0)==0x80
1157 ){
1158 *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
1159 | (z[3] & 0x3f);
1160 return 4;
1161 }
1162 *pU = 0;
1163 return 1;
1164 }
@@ -1189,18 +1217,28 @@
1217 ** since with %*.*s the width is measured in bytes, not characters.
1218 **
1219 ** Take into account zero-width and double-width Unicode characters.
1220 ** In other words, a zero-width character does not count toward the
1221 ** the w limit. A double-width character counts as two.
1222 **
1223 ** w should normally be a small number. A couple hundred at most. This
1224 ** routine caps w at 100 million to avoid integer overflow issues.
1225 */
1226 static void utf8_width_print(FILE *out, int w, const char *zUtf){
1227 const unsigned char *a = (const unsigned char*)zUtf;
1228 static const int mxW = 10000000;
1229 unsigned char c;
1230 int i = 0;
1231 int n = 0;
1232 int k;
1233 int aw;
1234 if( w<-mxW ){
1235 w = -mxW;
1236 }else if( w>mxW ){
1237 w= mxW;
1238 }
1239 aw = w<0 ? -w : w;
1240 if( zUtf==0 ) zUtf = "";
1241 while( (c = a[i])!=0 ){
1242 if( (c&0xc0)==0xc0 ){
1243 int u;
1244 int len = decodeUtf8(a+i, &u);
@@ -1323,11 +1361,11 @@
1361
1362 /*
1363 ** This routine reads a line of text from FILE in, stores
1364 ** the text in memory obtained from malloc() and returns a pointer
1365 ** to the text. NULL is returned at end of file, or if malloc()
1366 ** fails, or if the length of the line is longer than about a gigabyte.
1367 **
1368 ** If zLine is not NULL then it is a malloced buffer returned from
1369 ** a previous call to this routine that may be reused.
1370 */
1371 static char *local_getline(char *zLine, FILE *in){
@@ -1334,10 +1372,14 @@
1372 int nLine = zLine==0 ? 0 : 100;
1373 int n = 0;
1374
1375 while( 1 ){
1376 if( n+100>nLine ){
1377 if( nLine>=1073741773 ){
1378 free(zLine);
1379 return 0;
1380 }
1381 nLine = nLine*2 + 100;
1382 zLine = realloc(zLine, nLine);
1383 shell_check_oom(zLine);
1384 }
1385 if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
@@ -1417,14 +1459,18 @@
1459 return -1;
1460 }
1461
1462 /*
1463 ** Interpret zArg as an integer value, possibly with suffixes.
1464 **
1465 ** If the value specified by zArg is outside the range of values that
1466 ** can be represented using a 64-bit twos-complement integer, then return
1467 ** the nearest representable value.
1468 */
1469 static sqlite3_int64 integerValue(const char *zArg){
1470 sqlite3_uint64 v = 0;
1471 static const struct { char *zSuffix; unsigned int iMult; } aMult[] = {
1472 { "KiB", 1024 },
1473 { "MiB", 1024*1024 },
1474 { "GiB", 1024*1024*1024 },
1475 { "KB", 1000 },
1476 { "MB", 1000000 },
@@ -1443,46 +1489,54 @@
1489 }
1490 if( zArg[0]=='0' && zArg[1]=='x' ){
1491 int x;
1492 zArg += 2;
1493 while( (x = hexDigitValue(zArg[0]))>=0 ){
1494 if( v > 0x0fffffffffffffffULL ) goto integer_overflow;
1495 v = (v<<4) + x;
1496 zArg++;
1497 }
1498 }else{
1499 while( IsDigit(zArg[0]) ){
1500 if( v>=922337203685477580LL ){
1501 if( v>922337203685477580LL || zArg[0]>='8' ) goto integer_overflow;
1502 }
1503 v = v*10 + (zArg[0] - '0');
1504 zArg++;
1505 }
1506 }
1507 for(i=0; i<ArraySize(aMult); i++){
1508 if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
1509 if( 0x7fffffffffffffffULL/aMult[i].iMult < v ) goto integer_overflow;
1510 v *= aMult[i].iMult;
1511 break;
1512 }
1513 }
1514 if( isNeg && v>0x7fffffffffffffffULL ) goto integer_overflow;
1515 return isNeg? -(sqlite3_int64)v : (sqlite3_int64)v;
1516 integer_overflow:
1517 return isNeg ? (i64)0x8000000000000000LL : 0x7fffffffffffffffLL;
1518 }
1519
1520 /*
1521 ** A variable length string to which one can append text.
1522 */
1523 typedef struct ShellText ShellText;
1524 struct ShellText {
1525 char *zTxt; /* The text */
1526 i64 n; /* Number of bytes of zTxt[] actually used */
1527 i64 nAlloc; /* Number of bytes allocated for zTxt[] */
1528 };
1529
1530 /*
1531 ** Initialize and destroy a ShellText object
1532 */
1533 static void initText(ShellText *p){
1534 memset(p, 0, sizeof(*p));
1535 }
1536 static void freeText(ShellText *p){
1537 sqlite3_free(p->zTxt);
1538 initText(p);
1539 }
1540
1541 /* zIn is either a pointer to a NULL-terminated string in memory obtained
1542 ** from malloc(), or a NULL pointer. The string pointed to by zAppend is
@@ -1503,30 +1557,30 @@
1557 for(i=0; i<nAppend; i++){
1558 if( zAppend[i]==quote ) len++;
1559 }
1560 }
1561
1562 if( p->zTxt==0 || p->n+len>=p->nAlloc ){
1563 p->nAlloc = p->nAlloc*2 + len + 20;
1564 p->zTxt = sqlite3_realloc64(p->zTxt, p->nAlloc);
1565 shell_check_oom(p->zTxt);
1566 }
1567
1568 if( quote ){
1569 char *zCsr = p->zTxt+p->n;
1570 *zCsr++ = quote;
1571 for(i=0; i<nAppend; i++){
1572 *zCsr++ = zAppend[i];
1573 if( zAppend[i]==quote ) *zCsr++ = quote;
1574 }
1575 *zCsr++ = quote;
1576 p->n = (i64)(zCsr - p->zTxt);
1577 *zCsr = '\0';
1578 }else{
1579 memcpy(p->zTxt+p->n, zAppend, nAppend);
1580 p->n += nAppend;
1581 p->zTxt[p->n] = '\0';
1582 }
1583 }
1584
1585 /*
1586 ** Attempt to determine if identifier zName needs to be quoted, either
@@ -1547,10 +1601,13 @@
1601 }
1602
1603 /*
1604 ** Construct a fake object name and column list to describe the structure
1605 ** of the view, virtual table, or table valued function zSchema.zName.
1606 **
1607 ** The returned string comes from sqlite3_mprintf() and should be freed
1608 ** by the caller using sqlite3_free().
1609 */
1610 static char *shellFakeSchema(
1611 sqlite3 *db, /* The database connection containing the vtab */
1612 const char *zSchema, /* Schema of the database holding the vtab */
1613 const char *zName /* The name of the virtual table */
@@ -1587,13 +1644,13 @@
1644 }
1645 appendText(&s, ")", 0);
1646 sqlite3_finalize(pStmt);
1647 if( nRow==0 ){
1648 freeText(&s);
1649 s.zTxt = 0;
1650 }
1651 return s.zTxt;
1652 }
1653
1654 /*
1655 ** SQL function: strtod(X)
1656 **
@@ -1692,11 +1749,11 @@
1749 if( z==0 ){
1750 z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
1751 }else{
1752 z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
1753 }
1754 sqlite3_free(zFake);
1755 }
1756 if( z ){
1757 sqlite3_result_text(pCtx, z, -1, sqlite3_free);
1758 return;
1759 }
@@ -3667,11 +3724,12 @@
3724 iExp -= p->nFrac;
3725 p->nFrac = 0;
3726 }
3727 }
3728 if( iExp>0 ){
3729 p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
3730 + (sqlite3_int64)iExp + 1 );
3731 if( p->a==0 ) goto new_from_text_failed;
3732 memset(p->a+p->nDigit, 0, iExp);
3733 p->nDigit += iExp;
3734 }
3735 }else if( iExp<0 ){
@@ -3686,18 +3744,23 @@
3744 iExp -= nExtra;
3745 p->nFrac = p->nDigit - 1;
3746 }
3747 }
3748 if( iExp>0 ){
3749 p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
3750 + (sqlite3_int64)iExp + 1 );
3751 if( p->a==0 ) goto new_from_text_failed;
3752 memmove(p->a+iExp, p->a, p->nDigit);
3753 memset(p->a, 0, iExp);
3754 p->nDigit += iExp;
3755 p->nFrac += iExp;
3756 }
3757 }
3758 if( p->sign ){
3759 for(i=0; i<p->nDigit && p->a[i]==0; i++){}
3760 if( i>=p->nDigit ) p->sign = 0;
3761 }
3762 return p;
3763
3764 new_from_text_failed:
3765 if( p ){
3766 if( p->a ) sqlite3_free(p->a);
@@ -3786,11 +3849,11 @@
3849 }
3850 if( p->isNull ){
3851 sqlite3_result_null(pCtx);
3852 return;
3853 }
3854 z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 );
3855 if( z==0 ){
3856 sqlite3_result_error_nomem(pCtx);
3857 return;
3858 }
3859 i = 0;
@@ -3851,11 +3914,11 @@
3914 }
3915 for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
3916 for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
3917 nFrac = p->nFrac + (nDigit - p->nDigit);
3918 nDigit -= nZero;
3919 z = sqlite3_malloc64( (sqlite3_int64)nDigit+20 );
3920 if( z==0 ){
3921 sqlite3_result_error_nomem(pCtx);
3922 return;
3923 }
3924 if( nDigit==0 ){
@@ -3896,17 +3959,25 @@
3959 ** pA!=0
3960 ** pA->isNull==0
3961 ** pB!=0
3962 ** pB->isNull==0
3963 */
3964 static int decimal_cmp(Decimal *pA, Decimal *pB){
3965 int nASig, nBSig, rc, n;
3966 while( pA->nFrac>0 && pA->a[pA->nDigit-1]==0 ){
3967 pA->nDigit--;
3968 pA->nFrac--;
3969 }
3970 while( pB->nFrac>0 && pB->a[pB->nDigit-1]==0 ){
3971 pB->nDigit--;
3972 pB->nFrac--;
3973 }
3974 if( pA->sign!=pB->sign ){
3975 return pA->sign ? -1 : +1;
3976 }
3977 if( pA->sign ){
3978 Decimal *pTemp = pA;
3979 pA = pB;
3980 pB = pTemp;
3981 }
3982 nASig = pA->nDigit - pA->nFrac;
3983 nBSig = pB->nDigit - pB->nFrac;
@@ -4064,11 +4135,12 @@
4135 if( pA==0 || pA->oom || pA->isNull
4136 || pB==0 || pB->oom || pB->isNull
4137 ){
4138 goto mul_end;
4139 }
4140 acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
4141 (sqlite3_int64)pB->nDigit + 2 );
4142 if( acc==0 ){
4143 pA->oom = 1;
4144 goto mul_end;
4145 }
4146 memset(acc, 0, pA->nDigit + pB->nDigit + 2);
@@ -4151,11 +4223,11 @@
4223 r = -r;
4224 }else{
4225 isNeg = 0;
4226 }
4227 memcpy(&a,&r,sizeof(a));
4228 if( a==0 || a==(sqlite3_int64)0x8000000000000000LL){
4229 e = 0;
4230 m = 0;
4231 }else{
4232 e = a>>52;
4233 m = a & ((((sqlite3_int64)1)<<52)-1);
@@ -4426,516 +4498,10 @@
4498 }
4499 return rc;
4500 }
4501
4502 /************************* End ../ext/misc/decimal.c ********************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4503 #undef sqlite3_base_init
4504 #define sqlite3_base_init sqlite3_base64_init
4505 /************************* Begin ../ext/misc/base64.c ******************/
4506 /*
4507 ** 2022-11-18
@@ -5144,20 +4710,22 @@
4710 return pOut;
4711 }
4712
4713 /* This function does the work for the SQLite base64(x) UDF. */
4714 static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
4715 sqlite3_int64 nb;
4716 sqlite3_int64 nv = sqlite3_value_bytes(av[0]);
4717 sqlite3_int64 nc;
4718 int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
4719 SQLITE_LIMIT_LENGTH, -1);
4720 char *cBuf;
4721 u8 *bBuf;
4722 assert(na==1);
4723 switch( sqlite3_value_type(av[0]) ){
4724 case SQLITE_BLOB:
4725 nb = nv;
4726 nc = 4*((nv+2)/3); /* quads needed */
4727 nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
4728 if( nvMax < nc ){
4729 sqlite3_result_error(context, "blob expanded to base64 too big", -1);
4730 return;
4731 }
@@ -5524,11 +5092,11 @@
5092 }
5093 # endif
5094
5095 /* This function does the work for the SQLite base85(x) UDF. */
5096 static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
5097 sqlite3_int64 nb, nc, nv = sqlite3_value_bytes(av[0]);
5098 int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
5099 SQLITE_LIMIT_LENGTH, -1);
5100 char *cBuf;
5101 u8 *bBuf;
5102 assert(na==1);
@@ -5829,10 +5397,13 @@
5397 }
5398 memcpy(&a,&r,sizeof(a));
5399 if( a==0 ){
5400 e = 0;
5401 m = 0;
5402 }else if( a==(sqlite3_int64)0x8000000000000000LL ){
5403 e = -1996;
5404 m = -1;
5405 }else{
5406 e = a>>52;
5407 m = a & ((((sqlite3_int64)1)<<52)-1);
5408 if( e==0 ){
5409 m <<= 1;
@@ -6052,23 +5623,24 @@
5623 ** Examples:
5624 **
5625 ** SELECT * FROM generate_series(0,100,5);
5626 **
5627 ** The query above returns integers from 0 through 100 counting by steps
5628 ** of 5. In other words, 0, 5, 10, 15, ..., 90, 95, 100. There are a total
5629 ** of 21 rows.
5630 **
5631 ** SELECT * FROM generate_series(0,100);
5632 **
5633 ** Integers from 0 through 100 with a step size of 1. 101 rows.
5634 **
5635 ** SELECT * FROM generate_series(20) LIMIT 10;
5636 **
5637 ** Integers 20 through 29. 10 rows.
5638 **
5639 ** SELECT * FROM generate_series(0,-100,-5);
5640 **
5641 ** Integers 0 -5 -10 ... -100. 21 rows.
5642 **
5643 ** SELECT * FROM generate_series(0,-1);
5644 **
5645 ** Empty sequence.
5646 **
@@ -6140,143 +5712,92 @@
5712 #include <string.h>
5713 #include <limits.h>
5714 #include <math.h>
5715
5716 #ifndef SQLITE_OMIT_VIRTUALTABLE
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5717
5718 /* series_cursor is a subclass of sqlite3_vtab_cursor which will
5719 ** serve as the underlying representation of a cursor that scans
5720 ** over rows of the result.
5721 **
5722 ** iOBase, iOTerm, and iOStep are the original values of the
5723 ** start=, stop=, and step= constraints on the query. These are
5724 ** the values reported by the start, stop, and step columns of the
5725 ** virtual table.
5726 **
5727 ** iBase, iTerm, iStep, and bDescp are the actual values used to generate
5728 ** the sequence. These might be different from the iOxxxx values.
5729 ** For example in
5730 **
5731 ** SELECT value FROM generate_series(1,11,2)
5732 ** WHERE value BETWEEN 4 AND 8;
5733 **
5734 ** The iOBase is 1, but the iBase is 5. iOTerm is 11 but iTerm is 7.
5735 ** Another example:
5736 **
5737 ** SELECT value FROM generate_series(1,15,3) ORDER BY value DESC;
5738 **
5739 ** The cursor initialization for the above query is:
5740 **
5741 ** iOBase = 1 iBase = 13
5742 ** iOTerm = 15 iTerm = 1
5743 ** iOStep = 3 iStep = 3 bDesc = 1
5744 **
5745 ** The actual step size is unsigned so that can have a value of
5746 ** +9223372036854775808 which is needed for querys like this:
5747 **
5748 ** SELECT value
5749 ** FROM generate_series(9223372036854775807,
5750 ** -9223372036854775808,
5751 ** -9223372036854775808)
5752 ** ORDER BY value ASC;
5753 **
5754 ** The setup for the previous query will be:
5755 **
5756 ** iOBase = 9223372036854775807 iBase = -1
5757 ** iOTerm = -9223372036854775808 iTerm = 9223372036854775807
5758 ** iOStep = -9223372036854775808 iStep = 9223372036854775808 bDesc = 0
5759 */
5760 /* typedef unsigned char u8; */
5761 typedef struct series_cursor series_cursor;
5762 struct series_cursor {
5763 sqlite3_vtab_cursor base; /* Base class - must be first */
5764 sqlite3_int64 iOBase; /* Original starting value ("start") */
5765 sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
5766 sqlite3_int64 iOStep; /* Original step value */
5767 sqlite3_int64 iBase; /* Starting value to actually use */
5768 sqlite3_int64 iTerm; /* Terminal value to actually use */
5769 sqlite3_uint64 iStep; /* The step size */
5770 sqlite3_int64 iValue; /* Current value */
5771 u8 bDesc; /* iStep is really negative */
5772 u8 bDone; /* True if stepped past last element */
5773 };
5774
5775 /*
5776 ** Computed the difference between two 64-bit signed integers using a
5777 ** convoluted computation designed to work around the silly restriction
5778 ** against signed integer overflow in C.
5779 */
5780 static sqlite3_uint64 span64(sqlite3_int64 a, sqlite3_int64 b){
5781 assert( a>=b );
5782 return (*(sqlite3_uint64*)&a) - (*(sqlite3_uint64*)&b);
5783 }
5784
5785 /*
5786 ** Add or substract an unsigned 64-bit integer from a signed 64-bit integer
5787 ** and return the new signed 64-bit integer.
5788 */
5789 static sqlite3_int64 add64(sqlite3_int64 a, sqlite3_uint64 b){
5790 sqlite3_uint64 x = *(sqlite3_uint64*)&a;
5791 x += b;
5792 return *(sqlite3_int64*)&x;
5793 }
5794 static sqlite3_int64 sub64(sqlite3_int64 a, sqlite3_uint64 b){
5795 sqlite3_uint64 x = *(sqlite3_uint64*)&a;
5796 x -= b;
5797 return *(sqlite3_int64*)&x;
5798 }
5799
5800 /*
5801 ** The seriesConnect() method is invoked to create a new
5802 ** series_vtab that describes the generate_series virtual table.
5803 **
@@ -6354,11 +5875,19 @@
5875 /*
5876 ** Advance a series_cursor to its next row of output.
5877 */
5878 static int seriesNext(sqlite3_vtab_cursor *cur){
5879 series_cursor *pCur = (series_cursor*)cur;
5880 if( pCur->iValue==pCur->iTerm ){
5881 pCur->bDone = 1;
5882 }else if( pCur->bDesc ){
5883 pCur->iValue = sub64(pCur->iValue, pCur->iStep);
5884 assert( pCur->iValue>=pCur->iTerm );
5885 }else{
5886 pCur->iValue = add64(pCur->iValue, pCur->iStep);
5887 assert( pCur->iValue<=pCur->iTerm );
5888 }
5889 return SQLITE_OK;
5890 }
5891
5892 /*
5893 ** Return values of columns for the row at which the series_cursor
@@ -6370,41 +5899,41 @@
5899 int i /* Which column to return */
5900 ){
5901 series_cursor *pCur = (series_cursor*)cur;
5902 sqlite3_int64 x = 0;
5903 switch( i ){
5904 case SERIES_COLUMN_START: x = pCur->iOBase; break;
5905 case SERIES_COLUMN_STOP: x = pCur->iOTerm; break;
5906 case SERIES_COLUMN_STEP: x = pCur->iOStep; break;
5907 default: x = pCur->iValue; break;
5908 }
5909 sqlite3_result_int64(ctx, x);
5910 return SQLITE_OK;
5911 }
5912
5913 #ifndef LARGEST_UINT64
5914 #define LARGEST_INT64 ((sqlite3_int64)0x7fffffffffffffffLL)
5915 #define LARGEST_UINT64 ((sqlite3_uint64)0xffffffffffffffffULL)
5916 #define SMALLEST_INT64 ((sqlite3_int64)0x8000000000000000LL)
5917 #endif
5918
5919 /*
5920 ** The rowid is the same as the value.
5921 */
5922 static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
5923 series_cursor *pCur = (series_cursor*)cur;
5924 *pRowid = pCur->iValue;
5925 return SQLITE_OK;
5926 }
5927
5928 /*
5929 ** Return TRUE if the cursor has been moved off of the last
5930 ** row of output.
5931 */
5932 static int seriesEof(sqlite3_vtab_cursor *cur){
5933 series_cursor *pCur = (series_cursor*)cur;
5934 return pCur->bDone;
5935 }
5936
5937 /* True to cause run-time checking of the start=, stop=, and/or step=
5938 ** parameters. The only reason to do this is for testing the
5939 ** constraint checking logic for virtual tables in the SQLite core.
@@ -6411,10 +5940,63 @@
5940 */
5941 #ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
5942 # define SQLITE_SERIES_CONSTRAINT_VERIFY 0
5943 #endif
5944
5945 /*
5946 ** Return the number of steps between pCur->iBase and pCur->iTerm if
5947 ** the step width is pCur->iStep.
5948 */
5949 static sqlite3_uint64 seriesSteps(series_cursor *pCur){
5950 if( pCur->bDesc ){
5951 assert( pCur->iBase >= pCur->iTerm );
5952 return span64(pCur->iBase, pCur->iTerm)/pCur->iStep;
5953 }else{
5954 assert( pCur->iBase <= pCur->iTerm );
5955 return span64(pCur->iTerm, pCur->iBase)/pCur->iStep;
5956 }
5957 }
5958
5959 #if defined(SQLITE_ENABLE_MATH_FUNCTIONS) || defined(_WIN32)
5960 /*
5961 ** Case 1 (the most common case):
5962 ** The standard math library is available so use ceil() and floor() from there.
5963 */
5964 static double seriesCeil(double r){ return ceil(r); }
5965 static double seriesFloor(double r){ return floor(r); }
5966 #elif defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
5967 /*
5968 ** Case 2 (2nd most common): Use GCC/Clang builtins
5969 */
5970 static double seriesCeil(double r){ return __builtin_ceil(r); }
5971 static double seriesFloor(double r){ return __builtin_floor(r); }
5972 #else
5973 /*
5974 ** Case 3 (rarely happens): Use home-grown ceil() and floor() routines.
5975 */
5976 static double seriesCeil(double r){
5977 sqlite3_int64 x;
5978 if( r!=r ) return r;
5979 if( r<=(-4503599627370496.0) ) return r;
5980 if( r>=(+4503599627370496.0) ) return r;
5981 x = (sqlite3_int64)r;
5982 if( r==(double)x ) return r;
5983 if( r>(double)x ) x++;
5984 return (double)x;
5985 }
5986 static double seriesFloor(double r){
5987 sqlite3_int64 x;
5988 if( r!=r ) return r;
5989 if( r<=(-4503599627370496.0) ) return r;
5990 if( r>=(+4503599627370496.0) ) return r;
5991 x = (sqlite3_int64)r;
5992 if( r==(double)x ) return r;
5993 if( r<(double)x ) x--;
5994 return (double)x;
5995 }
5996 #endif
5997
5998 /*
5999 ** This method is called to "rewind" the series_cursor object back
6000 ** to the first row of output. This method is always called at least
6001 ** once prior to any call to seriesColumn() or seriesRowid() or
6002 ** seriesEof().
@@ -6444,185 +6026,243 @@
6026 sqlite3_vtab_cursor *pVtabCursor,
6027 int idxNum, const char *idxStrUnused,
6028 int argc, sqlite3_value **argv
6029 ){
6030 series_cursor *pCur = (series_cursor *)pVtabCursor;
6031 int iArg = 0; /* Arguments used so far */
6032 int i; /* Loop counter */
6033 sqlite3_int64 iMin = SMALLEST_INT64; /* Smallest allowed output value */
6034 sqlite3_int64 iMax = LARGEST_INT64; /* Largest allowed output value */
6035 sqlite3_int64 iLimit = 0; /* if >0, the value of the LIMIT */
6036 sqlite3_int64 iOffset = 0; /* if >0, the value of the OFFSET */
6037
6038 (void)idxStrUnused;
6039
6040 /* If any constraints have a NULL value, then return no rows.
6041 ** See ticket https://sqlite.org/src/info/fac496b61722daf2
6042 */
6043 for(i=0; i<argc; i++){
6044 if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
6045 goto series_no_rows;
6046 }
6047 }
6048
6049 /* Capture the three HIDDEN parameters to the virtual table and insert
6050 ** default values for any parameters that are omitted.
6051 */
6052 if( idxNum & 0x01 ){
6053 pCur->iOBase = sqlite3_value_int64(argv[iArg++]);
6054 }else{
6055 pCur->iOBase = 0;
6056 }
6057 if( idxNum & 0x02 ){
6058 pCur->iOTerm = sqlite3_value_int64(argv[iArg++]);
6059 }else{
6060 pCur->iOTerm = 0xffffffff;
6061 }
6062 if( idxNum & 0x04 ){
6063 pCur->iOStep = sqlite3_value_int64(argv[iArg++]);
6064 if( pCur->iOStep==0 ) pCur->iOStep = 1;
 
 
 
 
6065 }else{
6066 pCur->iOStep = 1;
6067 }
6068
6069 /* If there are constraints on the value column but there are
6070 ** no constraints on the start, stop, and step columns, then
6071 ** initialize the default range to be the entire range of 64-bit signed
6072 ** integers. This range will contracted by the value column constraints
6073 ** further below.
6074 */
6075 if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
6076 pCur->iOBase = SMALLEST_INT64;
6077 }
6078 if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
6079 pCur->iOTerm = LARGEST_INT64;
6080 }
6081 pCur->iBase = pCur->iOBase;
6082 pCur->iTerm = pCur->iOTerm;
6083 if( pCur->iOStep>0 ){
6084 pCur->iStep = pCur->iOStep;
6085 }else if( pCur->iOStep>SMALLEST_INT64 ){
6086 pCur->iStep = -pCur->iOStep;
6087 }else{
6088 pCur->iStep = LARGEST_INT64;
6089 pCur->iStep++;
6090 }
6091 pCur->bDesc = pCur->iOStep<0;
6092 if( pCur->bDesc==0 && pCur->iBase>pCur->iTerm ){
6093 goto series_no_rows;
6094 }
6095 if( pCur->bDesc!=0 && pCur->iBase<pCur->iTerm ){
6096 goto series_no_rows;
6097 }
6098
6099 /* Extract the LIMIT and OFFSET values, but do not apply them yet.
6100 ** The range must first be constrained by the limits on value.
6101 */
6102 if( idxNum & 0x20 ){
6103 iLimit = sqlite3_value_int64(argv[iArg++]);
6104 if( idxNum & 0x40 ){
6105 iOffset = sqlite3_value_int64(argv[iArg++]);
6106 }
6107 }
6108
6109 /* Narrow the range of iMin and iMax (the minimum and maximum outputs)
6110 ** based on equality and inequality constraints on the "value" column.
6111 */
6112 if( idxNum & 0x3380 ){
6113 if( idxNum & 0x0080 ){ /* value=X */
6114 if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
6115 double r = sqlite3_value_double(argv[iArg++]);
6116 if( r==seriesCeil(r)
6117 && r>=(double)SMALLEST_INT64
6118 && r<=(double)LARGEST_INT64
6119 ){
6120 iMin = iMax = (sqlite3_int64)r;
6121 }else{
6122 goto series_no_rows;
6123 }
6124 }else{
6125 iMin = iMax = sqlite3_value_int64(argv[iArg++]);
6126 }
6127 }else{
6128 if( idxNum & 0x0300 ){ /* value>X or value>=X */
6129 if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
6130 double r = sqlite3_value_double(argv[iArg++]);
6131 if( r<(double)SMALLEST_INT64 ){
6132 iMin = SMALLEST_INT64;
6133 }else if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){
6134 iMin = (sqlite3_int64)seriesCeil(r+1.0);
6135 }else{
6136 iMin = (sqlite3_int64)seriesCeil(r);
6137 }
6138 }else{
6139 iMin = sqlite3_value_int64(argv[iArg++]);
6140 if( (idxNum & 0x0200)!=0 ){
6141 if( iMin==LARGEST_INT64 ){
6142 goto series_no_rows;
6143 }else{
6144 iMin++;
6145 }
6146 }
6147 }
6148 }
6149 if( idxNum & 0x3000 ){ /* value<X or value<=X */
6150 if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
6151 double r = sqlite3_value_double(argv[iArg++]);
6152 if( r>(double)LARGEST_INT64 ){
6153 iMax = LARGEST_INT64;
6154 }else if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){
6155 iMax = (sqlite3_int64)(r-1.0);
6156 }else{
6157 iMax = (sqlite3_int64)seriesFloor(r);
6158 }
6159 }else{
6160 iMax = sqlite3_value_int64(argv[iArg++]);
6161 if( idxNum & 0x2000 ){
6162 if( iMax==SMALLEST_INT64 ){
6163 goto series_no_rows;
6164 }else{
6165 iMax--;
6166 }
6167 }
6168 }
6169 }
6170 if( iMin>iMax ){
6171 goto series_no_rows;
6172 }
6173 }
6174
6175 /* Try to reduce the range of values to be generated based on
6176 ** constraints on the "value" column.
6177 */
6178 if( pCur->bDesc==0 ){
6179 if( pCur->iBase<iMin ){
6180 sqlite3_uint64 span = span64(iMin,pCur->iBase);
6181 pCur->iBase = add64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
6182 if( pCur->iBase<iMin ){
6183 if( pCur->iBase > sub64(LARGEST_INT64, pCur->iStep) ){
6184 goto series_no_rows;
6185 }
6186 pCur->iBase = add64(pCur->iBase, pCur->iStep);
6187 }
6188 }
6189 if( pCur->iTerm>iMax ){
6190 pCur->iTerm = iMax;
6191 }
6192 }else{
6193 if( pCur->iBase>iMax ){
6194 sqlite3_uint64 span = span64(pCur->iBase,iMax);
6195 pCur->iBase = sub64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
6196 if( pCur->iBase>iMax ){
6197 if( pCur->iBase < add64(SMALLEST_INT64, pCur->iStep) ){
6198 goto series_no_rows;
6199 }
6200 pCur->iBase = sub64(pCur->iBase, pCur->iStep);
6201 }
6202 }
6203 if( pCur->iTerm<iMin ){
6204 pCur->iTerm = iMin;
6205 }
6206 }
6207 }
6208
6209 /* Adjust iTerm so that it is exactly the last value of the series.
6210 */
6211 if( pCur->bDesc==0 ){
6212 if( pCur->iBase>pCur->iTerm ){
6213 goto series_no_rows;
6214 }
6215 pCur->iTerm = sub64(pCur->iTerm,
6216 span64(pCur->iTerm,pCur->iBase) % pCur->iStep);
6217 }else{
6218 if( pCur->iBase<pCur->iTerm ){
6219 goto series_no_rows;
6220 }
6221 pCur->iTerm = add64(pCur->iTerm,
6222 span64(pCur->iBase,pCur->iTerm) % pCur->iStep);
6223 }
6224
6225 /* Transform the series generator to output values in the requested
6226 ** order.
6227 */
6228 if( ((idxNum & 0x0008)!=0 && pCur->bDesc==0)
6229 || ((idxNum & 0x0010)!=0 && pCur->bDesc!=0)
6230 ){
6231 sqlite3_int64 tmp = pCur->iBase;
6232 pCur->iBase = pCur->iTerm;
6233 pCur->iTerm = tmp;
6234 pCur->bDesc = !pCur->bDesc;
6235 }
6236
6237 /* Apply LIMIT and OFFSET constraints, if any */
6238 assert( pCur->iStep!=0 );
6239 if( idxNum & 0x20 ){
6240 if( iOffset>0 ){
6241 if( seriesSteps(pCur) < (sqlite3_uint64)iOffset ){
6242 goto series_no_rows;
6243 }else if( pCur->bDesc ){
6244 pCur->iBase = sub64(pCur->iBase, pCur->iStep*iOffset);
6245 }else{
6246 pCur->iBase = add64(pCur->iBase, pCur->iStep*iOffset);
6247 }
6248 }
6249 if( iLimit>=0 && seriesSteps(pCur) > (sqlite3_uint64)iLimit ){
6250 pCur->iTerm = add64(pCur->iBase, (iLimit - 1)*pCur->iStep);
6251 }
6252 }
6253 pCur->iValue = pCur->iBase;
6254 pCur->bDone = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6255 return SQLITE_OK;
6256
6257 series_no_rows:
6258 pCur->iBase = 0;
6259 pCur->iTerm = 0;
6260 pCur->iStep = 1;
6261 pCur->bDesc = 0;
6262 pCur->bDone = 1;
6263 return SQLITE_OK;
6264 }
6265
6266 /*
6267 ** SQLite will invoke this method one or more times while planning a query
6268 ** that uses the generate_series virtual table. This routine needs to create
@@ -6948,10 +6588,12 @@
6588 ** expression and M is the size of the input string. The matcher never
6589 ** exhibits exponential behavior. Note that the X{p,q} operator expands
6590 ** to p copies of X following by q-p copies of X? and that the size of the
6591 ** regular expression in the O(N*M) performance bound is computed after
6592 ** this expansion.
6593 **
6594 ** To help prevent DoS attacks, the maximum size of the NFA is restricted.
6595 */
6596 #include <string.h>
6597 #include <stdlib.h>
6598 /* #include "sqlite3ext.h" */
6599 SQLITE_EXTENSION_INIT1
@@ -6988,36 +6630,10 @@
6630 #define RE_OP_NOTDIGIT 14 /* Not a digit */
6631 #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
6632 #define RE_OP_NOTSPACE 16 /* Not a digit */
6633 #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
6634 #define RE_OP_ATSTART 18 /* Currently at the start of the string */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6635
6636 /* Each opcode is a "state" in the NFA */
6637 typedef unsigned short ReStateNumber;
6638
6639 /* Because this is an NFA and not a DFA, multiple states can be active at
@@ -7051,10 +6667,11 @@
6667 unsigned (*xNextChar)(ReInput*); /* Next character function */
6668 unsigned char zInit[12]; /* Initial text to match */
6669 int nInit; /* Number of bytes in zInit */
6670 unsigned nState; /* Number of entries in aOp[] and aArg[] */
6671 unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
6672 unsigned mxAlloc; /* Complexity limit */
6673 };
6674
6675 /* Add a state to the given state set if it is not already there */
6676 static void re_add_state(ReStateSet *pSet, int newState){
6677 unsigned i;
@@ -7265,18 +6882,19 @@
6882 return rc;
6883 }
6884
6885 /* Resize the opcode and argument arrays for an RE under construction.
6886 */
6887 static int re_resize(ReCompiled *p, unsigned int N){
6888 char *aOp;
6889 int *aArg;
6890 if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
6891 aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
6892 if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
6893 p->aOp = aOp;
6894 aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
6895 if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
6896 p->aArg = aArg;
6897 p->nAlloc = N;
6898 return 0;
6899 }
6900
@@ -7303,11 +6921,11 @@
6921 }
6922
6923 /* Make a copy of N opcodes starting at iStart onto the end of the RE
6924 ** under construction.
6925 */
6926 static void re_copy(ReCompiled *p, int iStart, unsigned int N){
6927 if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
6928 memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
6929 memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
6930 p->nState += N;
6931 }
@@ -7456,22 +7074,30 @@
7074 case '^': {
7075 re_append(p, RE_OP_ATSTART, 0);
7076 break;
7077 }
7078 case '{': {
7079 unsigned int m = 0, n = 0;
7080 unsigned int sz, j;
7081 if( iPrev<0 ) return "'{m,n}' without operand";
7082 while( (c=rePeek(p))>='0' && c<='9' ){
7083 m = m*10 + c - '0';
7084 if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
7085 p->sIn.i++;
7086 }
7087 n = m;
7088 if( c==',' ){
7089 p->sIn.i++;
7090 n = 0;
7091 while( (c=rePeek(p))>='0' && c<='9' ){
7092 n = n*10 + c-'0';
7093 if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
7094 p->sIn.i++;
7095 }
7096 }
7097 if( c!='}' ) return "unmatched '{'";
7098 if( n<m ) return "n less than m in '{m,n}'";
7099 p->sIn.i++;
7100 sz = p->nState - iPrev;
7101 if( m==0 ){
7102 if( n==0 ) return "both m and n are zero in '{m,n}'";
7103 re_insert(p, iPrev, RE_OP_FORK, sz+1);
@@ -7483,11 +7109,11 @@
7109 for(j=m; j<n; j++){
7110 re_append(p, RE_OP_FORK, sz+1);
7111 re_copy(p, iPrev, sz);
7112 }
7113 if( n==0 && m>0 ){
7114 re_append(p, RE_OP_FORK, -(int)sz);
7115 }
7116 break;
7117 }
7118 case '[': {
7119 unsigned int iFirst = p->nState;
@@ -7549,12 +7175,11 @@
7175
7176 /* Free and reclaim all the memory used by a previously compiled
7177 ** regular expression. Applications should invoke this routine once
7178 ** for every call to re_compile() to avoid memory leaks.
7179 */
7180 static void re_free(ReCompiled *pRe){
 
7181 if( pRe ){
7182 sqlite3_free(pRe->aOp);
7183 sqlite3_free(pRe->aArg);
7184 sqlite3_free(pRe);
7185 }
@@ -7564,11 +7189,16 @@
7189 ** Compile a textual regular expression in zIn[] into a compiled regular
7190 ** expression suitable for us by re_match() and return a pointer to the
7191 ** compiled regular expression in *ppRe. Return NULL on success or an
7192 ** error message if something goes wrong.
7193 */
7194 static const char *re_compile(
7195 ReCompiled **ppRe, /* OUT: write compiled NFA here */
7196 const char *zIn, /* Input regular expression */
7197 int mxRe, /* Complexity limit */
7198 int noCase /* True for caseless comparisons */
7199 ){
7200 ReCompiled *pRe;
7201 const char *zErr;
7202 int i, j;
7203
7204 *ppRe = 0;
@@ -7576,13 +7206,15 @@
7206 if( pRe==0 ){
7207 return "out of memory";
7208 }
7209 memset(pRe, 0, sizeof(*pRe));
7210 pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
7211 pRe->mxAlloc = mxRe;
7212 if( re_resize(pRe, 30) ){
7213 zErr = pRe->zErr;
7214 re_free(pRe);
7215 return zErr;
7216 }
7217 if( zIn[0]=='^' ){
7218 zIn++;
7219 }else{
7220 re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -7630,10 +7262,18 @@
7262 if( j>0 && pRe->zInit[j-1]==0 ) j--;
7263 pRe->nInit = j;
7264 }
7265 return pRe->zErr;
7266 }
7267
7268 /*
7269 ** Compute a reasonable limit on the length of the REGEXP NFA.
7270 */
7271 static int re_maxlen(sqlite3_context *context){
7272 sqlite3 *db = sqlite3_context_db_handle(context);
7273 return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2;
7274 }
7275
7276 /*
7277 ** Implementation of the regexp() SQL function. This function implements
7278 ** the build-in REGEXP operator. The first argument to the function is the
7279 ** pattern and the second argument is the string. So, the SQL statements:
@@ -7656,11 +7296,12 @@
7296 (void)argc; /* Unused */
7297 pRe = sqlite3_get_auxdata(context, 0);
7298 if( pRe==0 ){
7299 zPattern = (const char*)sqlite3_value_text(argv[0]);
7300 if( zPattern==0 ) return;
7301 zErr = re_compile(&pRe, zPattern, re_maxlen(context),
7302 sqlite3_user_data(context)!=0);
7303 if( zErr ){
7304 re_free(pRe);
7305 sqlite3_result_error(context, zErr, -1);
7306 return;
7307 }
@@ -7698,14 +7339,36 @@
7339 sqlite3_str *pStr;
7340 int i;
7341 int n;
7342 char *z;
7343 (void)argc;
7344 static const char *ReOpName[] = {
7345 "EOF",
7346 "MATCH",
7347 "ANY",
7348 "ANYSTAR",
7349 "FORK",
7350 "GOTO",
7351 "ACCEPT",
7352 "CC_INC",
7353 "CC_EXC",
7354 "CC_VALUE",
7355 "CC_RANGE",
7356 "WORD",
7357 "NOTWORD",
7358 "DIGIT",
7359 "NOTDIGIT",
7360 "SPACE",
7361 "NOTSPACE",
7362 "BOUNDARY",
7363 "ATSTART",
7364 };
7365
7366 zPattern = (const char*)sqlite3_value_text(argv[0]);
7367 if( zPattern==0 ) return;
7368 zErr = re_compile(&pRe, zPattern, re_maxlen(context),
7369 sqlite3_user_data(context)!=0);
7370 if( zErr ){
7371 re_free(pRe);
7372 sqlite3_result_error(context, zErr, -1);
7373 return;
7374 }
@@ -9284,18 +8947,20 @@
8947 if( idxNum & 1 ){
8948 pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
8949 if( pCur->nPrefix>0 ){
8950 pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
8951 if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
8952 pCur->nPrefix = (int)strlen(pCur->zPrefix);
8953 }
8954 iArg = 1;
8955 }
8956 if( idxNum & 2 ){
8957 pCur->nLine = sqlite3_value_bytes(argv[iArg]);
8958 if( pCur->nLine>0 ){
8959 pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
8960 if( pCur->zLine==0 ) return SQLITE_NOMEM;
8961 pCur->nLine = (int)strlen(pCur->zLine);
8962 }
8963 }
8964 if( pCur->zLine!=0 && pCur->zPrefix==0 ){
8965 int i = pCur->nLine;
8966 while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
@@ -9303,10 +8968,11 @@
8968 }
8969 pCur->nPrefix = pCur->nLine - i;
8970 if( pCur->nPrefix>0 ){
8971 pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
8972 if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
8973 pCur->nPrefix = (int)strlen(pCur->zPrefix);
8974 }
8975 }
8976 pCur->iRowid = 0;
8977 pCur->ePhase = COMPLETION_FIRST_PHASE;
8978 return completionNext(pVtabCursor);
@@ -10216,11 +9882,17 @@
9882 "method," /* 6: Compression method (integer) */
9883 "z HIDDEN" /* 7: Name of zip file */
9884 ") WITHOUT ROWID;";
9885
9886 #define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
9887 #define ZIPFILE_MX_NAME (250) /* Windows limitation on filename size */
9888
9889 /*
9890 ** The buffer should be large enough to contain 3 65536 byte strings - the
9891 ** filename, the extra field and the file comment.
9892 */
9893 #define ZIPFILE_BUFFER_SIZE (200*1024)
9894
9895
9896 /*
9897 ** Magic numbers used to read and write zip files.
9898 **
@@ -10773,10 +10445,11 @@
10445 pLFH->crc32 = zipfileRead32(aRead);
10446 pLFH->szCompressed = zipfileRead32(aRead);
10447 pLFH->szUncompressed = zipfileRead32(aRead);
10448 pLFH->nFile = zipfileRead16(aRead);
10449 pLFH->nExtra = zipfileRead16(aRead);
10450 if( pLFH->nFile>ZIPFILE_MX_NAME ) rc = SQLITE_ERROR;
10451 }
10452 return rc;
10453 }
10454
10455
@@ -10898,10 +10571,19 @@
10571 || mUnixTime==zipfileMtime(pCds)
10572 || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
10573 /* || (mUnixTime % 2) */
10574 );
10575 }
10576
10577 /*
10578 ** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a
10579 ** generic corruption message and return SQLITE_CORRUPT;
10580 */
10581 static int zipfileCorrupt(char **pzErr){
10582 *pzErr = sqlite3_mprintf("zip archive is corrupt");
10583 return SQLITE_CORRUPT;
10584 }
10585
10586 /*
10587 ** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
10588 ** size) containing an entire zip archive image. Or, if aBlob is NULL,
10589 ** then pFile is a file-handle open on a zip file. In either case, this
@@ -10921,16 +10603,19 @@
10603 ZipfileEntry **ppEntry /* OUT: Pointer to new object */
10604 ){
10605 u8 *aRead;
10606 char **pzErr = &pTab->base.zErrMsg;
10607 int rc = SQLITE_OK;
 
10608
10609 if( aBlob==0 ){
10610 aRead = pTab->aBuffer;
10611 rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
10612 }else{
10613 if( (iOff+ZIPFILE_CDS_FIXED_SZ)>nBlob ){
10614 /* Not enough data for the CDS structure. Corruption. */
10615 return zipfileCorrupt(pzErr);
10616 }
10617 aRead = (u8*)&aBlob[iOff];
10618 }
10619
10620 if( rc==SQLITE_OK ){
10621 sqlite3_int64 nAlloc;
@@ -10957,10 +10642,13 @@
10642 rc = zipfileReadData(
10643 pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
10644 );
10645 }else{
10646 aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
10647 if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){
10648 rc = zipfileCorrupt(pzErr);
10649 }
10650 }
10651 }
10652
10653 if( rc==SQLITE_OK ){
10654 u32 *pt = &pNew->mUnixTime;
@@ -10979,19 +10667,26 @@
10667 ZipfileLFH lfh;
10668 if( pFile ){
10669 rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
10670 }else{
10671 aRead = (u8*)&aBlob[pNew->cds.iOffset];
10672 if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
10673 rc = zipfileCorrupt(pzErr);
10674 }
10675 }
10676
10677 if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
10678 if( rc==SQLITE_OK ){
10679 pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
10680 pNew->iDataOff += lfh.nFile + lfh.nExtra;
10681 if( aBlob && pNew->cds.szCompressed ){
10682 if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
10683 rc = zipfileCorrupt(pzErr);
10684 }else{
10685 pNew->aData = &pNew->aExtra[nExtra];
10686 memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
10687 }
10688 }
10689 }else{
10690 *pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
10691 (int)pNew->cds.iOffset
10692 );
@@ -11774,10 +11469,15 @@
11469
11470 if( rc==SQLITE_OK ){
11471 zPath = (const char*)sqlite3_value_text(apVal[2]);
11472 if( zPath==0 ) zPath = "";
11473 nPath = (int)strlen(zPath);
11474 if( nPath>ZIPFILE_MX_NAME ){
11475 zipfileTableErr(pTab, "filename too long; max: %d bytes",
11476 ZIPFILE_MX_NAME);
11477 rc = SQLITE_CONSTRAINT;
11478 }
11479 mTime = zipfileGetTime(apVal[4]);
11480 }
11481
11482 if( rc==SQLITE_OK && bIsDir ){
11483 /* For a directory, check that the last character in the path is a
@@ -12135,10 +11835,17 @@
11835 if( zName==0 ){
11836 zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
11837 rc = SQLITE_ERROR;
11838 goto zipfile_step_out;
11839 }
11840 if( nName>ZIPFILE_MX_NAME ){
11841 zErr = sqlite3_mprintf(
11842 "filename argument to zipfile() too big; max: %d bytes",
11843 ZIPFILE_MX_NAME);
11844 rc = SQLITE_ERROR;
11845 goto zipfile_step_out;
11846 }
11847
11848 /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
11849 ** deflate compression) or NULL (choose automatically). */
11850 if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
11851 iMethod = (int)sqlite3_value_int64(pMethod);
@@ -12477,10 +12184,11 @@
12184 return rc;
12185 }
12186
12187 /************************* End ../ext/misc/sqlar.c ********************/
12188 #endif
12189 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
12190 /************************* Begin ../ext/expert/sqlite3expert.h ******************/
12191 /*
12192 ** 2017 April 07
12193 **
12194 ** The author disclaims copyright to this source code. In place of
@@ -14885,10 +14593,11 @@
14593 }
14594
14595 #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
14596
14597 /************************* End ../ext/expert/sqlite3expert.c ********************/
14598 #endif
14599 /************************* Begin ../ext/intck/sqlite3intck.h ******************/
14600 /*
14601 ** 2024-02-08
14602 **
14603 ** The author disclaims copyright to this source code. In place of
@@ -21525,15 +21234,17 @@
21234 char **azFilter; /* Array of xFilter rejection GLOB patterns */
21235 sqlite3_session *p; /* The open session */
21236 };
21237 #endif
21238
21239 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
21240 typedef struct ExpertInfo ExpertInfo;
21241 struct ExpertInfo {
21242 sqlite3expert *pExpert;
21243 int bVerbose;
21244 };
21245 #endif
21246
21247 /* A single line in the EQP output */
21248 typedef struct EQPGraphRow EQPGraphRow;
21249 struct EQPGraphRow {
21250 int iEqpId; /* ID for this row */
@@ -21633,11 +21344,13 @@
21344 int *aiIndent; /* Array of indents used in MODE_Explain */
21345 int nIndent; /* Size of array aiIndent[] */
21346 int iIndent; /* Index of current op in aiIndent[] */
21347 char *zNonce; /* Nonce for temporary safe-mode escapes */
21348 EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
21349 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
21350 ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
21351 #endif
21352 #ifdef SQLITE_SHELL_FIDDLE
21353 struct {
21354 const char * zInput; /* Input string from wasm/JS proxy */
21355 const char * zPos; /* Cursor pos into zInput */
21356 const char * zDefaultDbName; /* Default name for db file */
@@ -21661,13 +21374,12 @@
21374 */
21375 #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
21376 #define SHELL_OPEN_NORMAL 1 /* Normal database file */
21377 #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
21378 #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
21379 #define SHELL_OPEN_DESERIALIZE 4 /* Open using sqlite3_deserialize() */
21380 #define SHELL_OPEN_HEXDB 5 /* Use "dbtotxt" output as data source */
 
21381
21382 /* Allowed values for ShellState.eTraceType
21383 */
21384 #define SHELL_TRACE_PLAIN 0 /* Show input SQL text */
21385 #define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */
@@ -22006,11 +21718,11 @@
21718 */
21719 static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
21720 int i;
21721 unsigned char *aBlob = (unsigned char*)pBlob;
21722
21723 char *zStr = sqlite3_malloc64((i64)nBlob*2 + 1);
21724 shell_check_oom(zStr);
21725
21726 for(i=0; i<nBlob; i++){
21727 static const char aHex[] = {
21728 '0', '1', '2', '3', '4', '5', '6', '7',
@@ -22027,11 +21739,11 @@
21739
21740 /*
21741 ** Output the given string as a quoted string using SQL quoting conventions:
21742 **
21743 ** (1) Single quotes (') within the string are doubled
21744 ** (2) The while string is enclosed in '...'
21745 ** (3) Control characters other than \n, \t, and \r\n are escaped
21746 ** using \u00XX notation and if such substitutions occur,
21747 ** the whole string is enclosed in unistr('...') instead of '...'.
21748 **
21749 ** Step (3) is omitted if the control-character escape mode is OFF.
@@ -22273,11 +21985,11 @@
21985 **
21986 ** Escaping is needed if the string contains any control characters
21987 ** other than \t, \n, and \r\n
21988 **
21989 ** If no escaping is needed (the common case) then set *ppFree to NULL
21990 ** and return the original string. If escaping is needed, write the
21991 ** escaped string into memory obtained from sqlite3_malloc64() or the
21992 ** equivalent, and return the new string and set *ppFree to the new string
21993 ** as well.
21994 **
21995 ** The caller is responsible for freeing *ppFree if it is non-NULL in order
@@ -23278,32 +22990,21 @@
22990 ** Set the destination table field of the ShellState structure to
22991 ** the name of the table given. Escape any quote characters in the
22992 ** table name.
22993 */
22994 static void set_table_name(ShellState *p, const char *zName){
 
 
 
 
22995 if( p->zDestTable ){
22996 sqlite3_free(p->zDestTable);
22997 p->zDestTable = 0;
22998 }
22999 if( zName==0 ) return;
23000 if( quoteChar(zName) ){
23001 p->zDestTable = sqlite3_mprintf("\"%w\"", zName);
23002 }else{
23003 p->zDestTable = sqlite3_mprintf("%s", zName);
23004 }
23005 shell_check_oom(p->zDestTable);
 
 
 
 
 
 
 
23006 }
23007
23008 /*
23009 ** Maybe construct two lines of text that point out the position of a
23010 ** syntax error. Return a pointer to the text, in memory obtained from
@@ -23496,12 +23197,12 @@
23197 static int display_stats(
23198 sqlite3 *db, /* Database to query */
23199 ShellState *pArg, /* Pointer to ShellState */
23200 int bReset /* True to reset the stats */
23201 ){
23202 int iCur, iHiwtr;
23203 sqlite3_int64 iCur64, iHiwtr64;
23204 FILE *out;
23205 if( pArg==0 || pArg->out==0 ) return 0;
23206 out = pArg->out;
23207
23208 if( pArg->pStmt && pArg->statsOn==2 ){
@@ -23586,18 +23287,25 @@
23287 "Page cache hits: %d\n", iCur);
23288 iHiwtr = iCur = -1;
23289 sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
23290 sqlite3_fprintf(out,
23291 "Page cache misses: %d\n", iCur);
23292 iHiwtr64 = iCur64 = -1;
23293 sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
23294 0);
23295 iHiwtr = iCur = -1;
23296 sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
23297 sqlite3_fprintf(out,
23298 "Page cache writes: %d\n", iCur);
23299 iHiwtr = iCur = -1;
23300 sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
23301 sqlite3_fprintf(out,
23302 "Page cache spills: %d\n", iCur);
23303 sqlite3_fprintf(out,
23304 "Temporary data spilled to disk: %lld\n", iCur64);
23305 sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
23306 1);
23307 iHiwtr = iCur = -1;
23308 sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
23309 sqlite3_fprintf(out,
23310 "Schema Heap Usage: %d bytes\n", iCur);
23311 iHiwtr = iCur = -1;
@@ -23999,10 +23707,40 @@
23707 char *zBuf = sqlite3_malloc64( szVar-5 );
23708 if( zBuf ){
23709 memcpy(zBuf, &zVar[6], szVar-5);
23710 sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
23711 }
23712 #ifdef SQLITE_ENABLE_CARRAY
23713 }else if( strncmp(zVar, "$carray_", 8)==0 ){
23714 static char *azColorNames[] = {
23715 "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold",
23716 "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon",
23717 "navy", "olive", "orange", "pink", "purple", "red", "silver",
23718 "tan", "teal", "violet", "white", "yellow"
23719 };
23720 static int aPrimes[] = {
23721 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
23722 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
23723 };
23724 /* Special bindings: carray($carray_clr), carray($carray_primes)
23725 ** with --unsafe-testing: carray($carray_clr_p,26,'char*'),
23726 ** carray($carray_primes_p,26,'int32')
23727 */
23728 if( strcmp(zVar+8,"clr")==0 ){
23729 sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0);
23730 }else if( strcmp(zVar+8,"primes")==0 ){
23731 sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0);
23732 }else if( strcmp(zVar+8,"clr_p")==0
23733 && ShellHasFlag(pArg,SHFLG_TestingMode) ){
23734 sqlite3_bind_pointer(pStmt,i,azColorNames,"carray",0);
23735 }else if( strcmp(zVar+8,"primes_p")==0
23736 && ShellHasFlag(pArg,SHFLG_TestingMode) ){
23737 sqlite3_bind_pointer(pStmt,i,aPrimes,"carray",0);
23738 }else{
23739 sqlite3_bind_null(pStmt, i);
23740 }
23741 #endif
23742 }else{
23743 sqlite3_bind_null(pStmt, i);
23744 }
23745 sqlite3_reset(pQ);
23746 }
@@ -24581,11 +24319,11 @@
24319 }
24320 }
24321 }
24322 }
24323
24324 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
24325 /*
24326 ** This function is called to process SQL if the previous shell command
24327 ** was ".expert". It passes the SQL in the second argument directly to
24328 ** the sqlite3expert object.
24329 **
@@ -24713,11 +24451,11 @@
24451 }
24452 sqlite3_free(zErr);
24453
24454 return rc;
24455 }
24456 #endif /* !SQLITE_OMIT_VIRTUALTABLE && !SQLITE_OMIT_AUTHORIZATION */
24457
24458 /*
24459 ** Execute a statement or set of statements. Print
24460 ** any result rows/columns depending on the current mode
24461 ** set via the supplied callback.
@@ -24739,11 +24477,11 @@
24477
24478 if( pzErrMsg ){
24479 *pzErrMsg = NULL;
24480 }
24481
24482 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
24483 if( pArg->expert.pExpert ){
24484 rc = expertHandleSQL(pArg, zSql, pzErrMsg);
24485 return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
24486 }
24487 #endif
@@ -24901,11 +24639,11 @@
24639 static char **tableColumnList(ShellState *p, const char *zTab){
24640 char **azCol = 0;
24641 sqlite3_stmt *pStmt;
24642 char *zSql;
24643 int nCol = 0;
24644 i64 nAlloc = 0;
24645 int nPK = 0; /* Number of PRIMARY KEY columns seen */
24646 int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
24647 int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid);
24648 int rc;
24649
@@ -24915,11 +24653,11 @@
24653 sqlite3_free(zSql);
24654 if( rc ) return 0;
24655 while( sqlite3_step(pStmt)==SQLITE_ROW ){
24656 if( nCol>=nAlloc-2 ){
24657 nAlloc = nAlloc*2 + nCol + 10;
24658 azCol = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
24659 shell_check_oom(azCol);
24660 }
24661 azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
24662 shell_check_oom(azCol[nCol]);
24663 if( sqlite3_column_int(pStmt, 5) ){
@@ -25106,17 +24844,17 @@
24844 appendText(&sSelect, " FROM ", 0);
24845 appendText(&sSelect, zTable, quoteChar(zTable));
24846
24847 savedDestTable = p->zDestTable;
24848 savedMode = p->mode;
24849 p->zDestTable = sTable.zTxt;
24850 p->mode = p->cMode = MODE_Insert;
24851 rc = shell_exec(p, sSelect.zTxt, 0);
24852 if( (rc&0xff)==SQLITE_CORRUPT ){
24853 sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
24854 toggleSelectOrder(p->db);
24855 shell_exec(p, sSelect.zTxt, 0);
24856 toggleSelectOrder(p->db);
24857 }
24858 p->zDestTable = savedDestTable;
24859 p->mode = savedMode;
24860 freeText(&sTable);
@@ -25245,11 +24983,13 @@
24983 " --bom Put a UTF8 byte-order mark on intermediate file",
24984 #endif
24985 #ifndef SQLITE_SHELL_FIDDLE
24986 ".exit ?CODE? Exit this program with return-code CODE",
24987 #endif
24988 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
24989 ".expert EXPERIMENTAL. Suggest indexes for queries",
24990 #endif
24991 ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
24992 ".filectrl CMD ... Run various sqlite3_file_control() operations",
24993 " --schema SCHEMA Use SCHEMA instead of \"main\"",
24994 " --help Show CMD details",
24995 ".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
@@ -25270,11 +25010,11 @@
25010 " from the \".mode\" output mode",
25011 " * If FILE begins with \"|\" then it is a command that generates the",
25012 " input text.",
25013 #endif
25014 #ifndef SQLITE_OMIT_TEST_CONTROL
25015 ".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
25016 #endif
25017 ".indexes ?TABLE? Show names of indexes",
25018 " If TABLE is specified, only show indexes for",
25019 " tables matching TABLE using the LIKE operator.",
25020 ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db",
@@ -25337,11 +25077,17 @@
25077 " Options:",
25078 " --append Use appendvfs to append database to the end of FILE",
25079 #endif
25080 #ifndef SQLITE_OMIT_DESERIALIZE
25081 " --deserialize Load into memory using sqlite3_deserialize()",
25082 #endif
25083 /*" --exclusive Set the SQLITE_OPEN_EXCLUSIVE flag", UNDOCUMENTED */
25084 #ifndef SQLITE_OMIT_DESERIALIZE
25085 " --hexdb Load the output of \"dbtotxt\" as an in-memory db",
25086 #endif
25087 " --ifexist Only open if FILE already exists",
25088 #ifndef SQLITE_OMIT_DESERIALIZE
25089 " --maxsize N Maximum size for --hexdb or --deserialized database",
25090 #endif
25091 " --new Initialize FILE to an empty database",
25092 " --nofollow Do not follow symbolic links",
25093 " --readonly Open FILE readonly",
@@ -25726,14 +25472,14 @@
25472 ** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
25473 */
25474 static unsigned char *readHexDb(ShellState *p, int *pnData){
25475 unsigned char *a = 0;
25476 int nLine;
25477 int n = 0; /* Size of db per first line of hex dump */
25478 i64 sz = 0; /* n rounded up to nearest page boundary */
25479 int pgsz = 0;
25480 i64 iOffset = 0;
 
25481 int rc;
25482 FILE *in;
25483 const char *zDbFilename = p->pAuxDb->zDbFilename;
25484 unsigned int x[16];
25485 char zLine[1000];
@@ -25753,20 +25499,21 @@
25499 nLine++;
25500 if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
25501 rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
25502 if( rc!=2 ) goto readHexDb_error;
25503 if( n<0 ) goto readHexDb_error;
 
 
 
 
 
25504 if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
25505 sqlite3_fputs("invalid pagesize\n", stderr);
25506 goto readHexDb_error;
25507 }
25508 sz = ((i64)n+pgsz-1)&~(pgsz-1); /* Round up to nearest multiple of pgsz */
25509 a = sqlite3_malloc64( sz ? sz : 1 );
25510 shell_check_oom(a);
25511 memset(a, 0, sz);
25512 for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
25513 int j = 0; /* Page number from "| page" line */
25514 int k = 0; /* Offset from "| page" line */
25515 rc = sscanf(zLine, "| page %d offset %d", &j, &k);
25516 if( rc==2 ){
25517 iOffset = k;
25518 continue;
25519 }
@@ -25775,18 +25522,18 @@
25522 }
25523 rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
25524 &j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
25525 &x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
25526 if( rc==17 ){
25527 i64 iOff = iOffset+j;
25528 if( iOff+16<=sz && iOff>=0 ){
25529 int ii;
25530 for(ii=0; ii<16; ii++) a[iOff+ii] = x[ii]&0xff;
25531 }
25532 }
25533 }
25534 *pnData = sz;
25535 if( in!=p->in ){
25536 fclose(in);
25537 }else{
25538 p->lineno = nLine;
25539 }
@@ -25849,11 +25596,11 @@
25596 p->pLog = pSavedLog;
25597
25598 if( zFake ){
25599 sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
25600 -1, sqlite3_free);
25601 sqlite3_free(zFake);
25602 }
25603 }
25604
25605 /* Flags for open_db().
25606 **
@@ -25881,14 +25628,17 @@
25628 }else{
25629 p->openMode = (u8)deduceDatabaseType(zDbFilename,
25630 (openFlags & OPEN_DB_ZIPFILE)!=0);
25631 }
25632 }
25633 if( (p->openFlags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE))==0 ){
25634 if( p->openFlags==0 ) p->openFlags = SQLITE_OPEN_CREATE;
25635 p->openFlags |= SQLITE_OPEN_READWRITE;
25636 }
25637 switch( p->openMode ){
25638 case SHELL_OPEN_APPENDVFS: {
25639 sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, "apndvfs");
 
25640 break;
25641 }
25642 case SHELL_OPEN_HEXDB:
25643 case SHELL_OPEN_DESERIALIZE: {
25644 sqlite3_open(0, &p->db);
@@ -25896,19 +25646,13 @@
25646 }
25647 case SHELL_OPEN_ZIPFILE: {
25648 sqlite3_open(":memory:", &p->db);
25649 break;
25650 }
 
 
 
 
 
25651 case SHELL_OPEN_UNSPEC:
25652 case SHELL_OPEN_NORMAL: {
25653 sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, 0);
 
25654 break;
25655 }
25656 }
25657 if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
25658 sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
@@ -25944,11 +25688,10 @@
25688 sqlite3_sha_init(p->db, 0, 0);
25689 sqlite3_shathree_init(p->db, 0, 0);
25690 sqlite3_uint_init(p->db, 0, 0);
25691 sqlite3_stmtrand_init(p->db, 0, 0);
25692 sqlite3_decimal_init(p->db, 0, 0);
 
25693 sqlite3_base64_init(p->db, 0, 0);
25694 sqlite3_base85_init(p->db, 0, 0);
25695 sqlite3_regexp_init(p->db, 0, 0);
25696 sqlite3_ieee_init(p->db, 0, 0);
25697 sqlite3_series_init(p->db, 0, 0);
@@ -26043,13 +25786,15 @@
25786 }
25787 }
25788 #endif
25789 }
25790 if( p->db!=0 ){
25791 #ifndef SQLITE_OMIT_AUTHORIZATION
25792 if( p->bSafeModePersist ){
25793 sqlite3_set_authorizer(p->db, safeModeAuth, p);
25794 }
25795 #endif
25796 sqlite3_db_config(
25797 p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
25798 );
25799 }
25800 }
@@ -26358,12 +26103,12 @@
26103 struct ImportCtx {
26104 const char *zFile; /* Name of the input file */
26105 FILE *in; /* Read the CSV text from this input stream */
26106 int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
26107 char *z; /* Accumulated text for a field */
26108 i64 n; /* Number of bytes in z */
26109 i64 nAlloc; /* Space allocated for z[] */
26110 int nLine; /* Current line number */
26111 int nRow; /* Number of rows imported */
26112 int nErr; /* Number of errors encountered */
26113 int bNotFirst; /* True if one or more bytes already read */
26114 int cTerm; /* Character that terminated the most recent field */
@@ -26829,14 +26574,17 @@
26574 #if SQLITE_SHELL_HAVE_RECOVER
26575 /*
26576 ** Convert a 2-byte or 4-byte big-endian integer into a native integer
26577 */
26578 static unsigned int get2byteInt(unsigned char *a){
26579 return ((unsigned int)a[0]<<8) + (unsigned int)a[1];
26580 }
26581 static unsigned int get4byteInt(unsigned char *a){
26582 return ((unsigned int)a[0]<<24)
26583 + ((unsigned int)a[1]<<16)
26584 + ((unsigned int)a[2]<<8)
26585 + (unsigned int)a[3];
26586 }
26587
26588 /*
26589 ** Implementation of the ".dbinfo" command.
26590 **
@@ -26969,11 +26717,11 @@
26717 if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
26718 nPage = sqlite3_column_int64(pStmt, 0);
26719 sqlite3_finalize(pStmt);
26720 pStmt = 0;
26721 if( nPage<1 ) goto dbtotxt_error;
26722 rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
26723 if( rc ) goto dbtotxt_error;
26724 if( sqlite3_step(pStmt)!=SQLITE_ROW ){
26725 zTail = "unk.db";
26726 }else{
26727 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
@@ -26980,10 +26728,15 @@
26728 if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
26729 zTail = strrchr(zFilename, '/');
26730 #if defined(_WIN32)
26731 if( zTail==0 ) zTail = strrchr(zFilename, '\\');
26732 #endif
26733 if( zTail==0 ){
26734 zTail = zFilename;
26735 }else if( zTail[1]!=0 ){
26736 zTail++;
26737 }
26738 }
26739 zName = strdup(zTail);
26740 shell_check_oom(zName);
26741 sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
26742 nPage*pgSz, pgSz, zName);
@@ -27148,10 +26901,47 @@
26901 if( zStr[0]!='-' ) return 0;
26902 zStr++;
26903 if( zStr[0]=='-' ) zStr++;
26904 return cli_strcmp(zStr, zOpt)==0;
26905 }
26906
26907 /*
26908 ** The input zFN is guaranteed to start with "file:" and is thus a URI
26909 ** filename. Extract the actual filename and return a pointer to that
26910 ** filename in spaced obtained from sqlite3_malloc().
26911 **
26912 ** The caller is responsible for freeing space using sqlite3_free() when
26913 ** it has finished with the filename.
26914 */
26915 static char *shellFilenameFromUri(const char *zFN){
26916 char *zOut;
26917 int i, j, d1, d2;
26918
26919 assert( cli_strncmp(zFN,"file:",5)==0 );
26920 zOut = sqlite3_mprintf("%s", zFN+5);
26921 shell_check_oom(zOut);
26922 for(i=j=0; zOut[i]!=0 && zOut[i]!='?'; i++){
26923 if( zOut[i]!='%' ){
26924 zOut[j++] = zOut[i];
26925 continue;
26926 }
26927 d1 = hexDigitValue(zOut[i+1]);
26928 if( d1<0 ){
26929 zOut[j] = 0;
26930 break;
26931 }
26932 d2 = hexDigitValue(zOut[i+2]);
26933 if( d2<0 ){
26934 zOut[j] = 0;
26935 break;
26936 }
26937 zOut[j++] = d1*16 + d2;
26938 i += 2;
26939 }
26940 zOut[j] = 0;
26941 return zOut;
26942 }
26943
26944 /*
26945 ** Delete a file.
26946 */
26947 int shellDeleteFile(const char *zFilename){
@@ -28704,11 +28494,11 @@
28494 int nArg = 0;
28495 int n, c;
28496 int rc = 0;
28497 char *azArg[52];
28498
28499 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
28500 if( p->expert.pExpert ){
28501 expertFinish(p, 1, 0);
28502 }
28503 #endif
28504
@@ -29005,11 +28795,11 @@
28795 }else{
28796 while( sqlite3_step(pStmt)==SQLITE_ROW ){
28797 const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
28798 const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
28799 if( zSchema==0 || zFile==0 ) continue;
28800 azName = sqlite3_realloc64(azName, (nName+1)*2*sizeof(char*));
28801 shell_check_oom(azName);
28802 azName[nName*2] = strdup(zSchema);
28803 azName[nName*2+1] = strdup(zFile);
28804 nName++;
28805 }
@@ -29208,10 +28998,11 @@
28998 rc = 1;
28999 }
29000 }else
29001
29002 if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
29003 open_db(p, 0);
29004 rc = shell_dbtotxt_command(p, nArg, azArg);
29005 }else
29006
29007 if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
29008 if( nArg==2 ){
@@ -29273,11 +29064,11 @@
29064 if( p->mode==MODE_Explain ) p->mode = p->normalMode;
29065 p->autoExplain = 1;
29066 }
29067 }else
29068
29069 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
29070 if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
29071 if( p->bSafeMode ){
29072 sqlite3_fprintf(stderr,
29073 "Cannot run experimental commands such as \"%s\" in safe mode\n",
29074 azArg[0]);
@@ -29840,16 +29631,10 @@
29631 sqlite3_stmt *pStmt;
29632 int tnum = 0;
29633 int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
29634 int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
29635 int i;
 
 
 
 
 
 
29636 if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
29637 eputz("Usage: .imposter INDEX IMPOSTER\n"
29638 " .imposter off\n");
29639 /* Also allowed, but not documented:
29640 **
@@ -29917,22 +29702,19 @@
29702 if( lenPK==0 ) lenPK = 100000;
29703 zSql = sqlite3_mprintf(
29704 "CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
29705 azArg[2], zCollist, lenPK, zCollist);
29706 sqlite3_free(zCollist);
29707 rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 2, tnum);
29708 if( rc==SQLITE_OK ){
29709 rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
29710 sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
29711 if( rc ){
29712 sqlite3_fprintf(stderr,
29713 "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
29714 }else{
29715 sqlite3_fprintf(stdout, "%s;\n", zSql);
 
 
 
29716 }
29717 }else{
29718 sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
29719 rc = 1;
29720 }
@@ -30282,10 +30064,11 @@
30064 const char *zFN = 0; /* Pointer to constant filename */
30065 char *zNewFilename = 0; /* Name of the database file to open */
30066 int iName = 1; /* Index in azArg[] of the filename */
30067 int newFlag = 0; /* True to delete file before opening */
30068 int openMode = SHELL_OPEN_UNSPEC;
30069 int openFlags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
30070
30071 /* Check for command-line arguments */
30072 for(iName=1; iName<nArg; iName++){
30073 const char *z = azArg[iName];
30074 #ifndef SQLITE_SHELL_FIDDLE
@@ -30296,13 +30079,18 @@
30079 openMode = SHELL_OPEN_ZIPFILE;
30080 #endif
30081 }else if( optionMatch(z, "append") ){
30082 openMode = SHELL_OPEN_APPENDVFS;
30083 }else if( optionMatch(z, "readonly") ){
30084 openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
30085 openFlags |= SQLITE_OPEN_READONLY;
30086 }else if( optionMatch(z, "exclusive") ){
30087 openFlags |= SQLITE_OPEN_EXCLUSIVE;
30088 }else if( optionMatch(z, "ifexists") ){
30089 openFlags &= ~(SQLITE_OPEN_CREATE);
30090 }else if( optionMatch(z, "nofollow") ){
30091 openFlags |= SQLITE_OPEN_NOFOLLOW;
30092 #ifndef SQLITE_OMIT_DESERIALIZE
30093 }else if( optionMatch(z, "deserialize") ){
30094 openMode = SHELL_OPEN_DESERIALIZE;
30095 }else if( optionMatch(z, "hexdb") ){
30096 openMode = SHELL_OPEN_HEXDB;
@@ -30330,16 +30118,25 @@
30118 p->db = 0;
30119 p->pAuxDb->zDbFilename = 0;
30120 sqlite3_free(p->pAuxDb->zFreeOnClose);
30121 p->pAuxDb->zFreeOnClose = 0;
30122 p->openMode = openMode;
30123 p->openFlags = openFlags;
30124 p->szMax = 0;
30125
30126 /* If a filename is specified, try to open it first */
30127 if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
30128 if( newFlag && zFN && !p->bSafeMode ){
30129 if( cli_strncmp(zFN,"file:",5)==0 ){
30130 char *zDel = shellFilenameFromUri(zFN);
30131 shell_check_oom(zDel);
30132 shellDeleteFile(zDel);
30133 sqlite3_free(zDel);
30134 }else{
30135 shellDeleteFile(zFN);
30136 }
30137 }
30138 #ifndef SQLITE_SHELL_FIDDLE
30139 if( p->bSafeMode
30140 && p->openMode!=SHELL_OPEN_HEXDB
30141 && zFN
30142 && cli_strcmp(zFN,":memory:")!=0
@@ -30938,13 +30735,13 @@
30735 appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
30736 }
30737 appendText(&sSelect, "sql IS NOT NULL"
30738 " ORDER BY snum, rowid", 0);
30739 if( bDebug ){
30740 sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.zTxt);
30741 }else{
30742 rc = sqlite3_exec(p->db, sSelect.zTxt, callback, &data, &zErrMsg);
30743 }
30744 freeText(&sSelect);
30745 }
30746 if( zErrMsg ){
30747 shellEmitError(zErrMsg);
@@ -31072,19 +30869,20 @@
30869
30870 /* .session filter GLOB ....
30871 ** Set a list of GLOB patterns of table names to be excluded.
30872 */
30873 if( cli_strcmp(azCmd[0], "filter")==0 ){
30874 int ii;
30875 i64 nByte;
30876 if( nCmd<2 ) goto session_syntax_error;
30877 if( pAuxDb->nSession ){
30878 for(ii=0; ii<pSession->nFilter; ii++){
30879 sqlite3_free(pSession->azFilter[ii]);
30880 }
30881 sqlite3_free(pSession->azFilter);
30882 nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
30883 pSession->azFilter = sqlite3_malloc64( nByte );
30884 shell_check_oom( pSession->azFilter );
30885 for(ii=1; ii<nCmd; ii++){
30886 char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
30887 shell_check_oom(x);
30888 }
@@ -31264,26 +31062,26 @@
31062 sqlite3_fprintf(p->out, "%s\n", zSql);
31063 }else
31064 if( cli_strcmp(zOp,"run")==0 ){
31065 char *zErrMsg = 0;
31066 str.n = 0;
31067 str.zTxt[0] = 0;
31068 rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
31069 nTest++;
31070 if( bVerbose ){
31071 sqlite3_fprintf(p->out, "Result: %s\n", str.zTxt);
31072 }
31073 if( rc || zErrMsg ){
31074 nErr++;
31075 rc = 1;
31076 sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
31077 sqlite3_free(zErrMsg);
31078 }else if( cli_strcmp(zAns,str.zTxt)!=0 ){
31079 nErr++;
31080 rc = 1;
31081 sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns);
31082 sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.zTxt);
31083 }
31084 }
31085 else{
31086 sqlite3_fprintf(stderr,
31087 "Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
@@ -31395,11 +31193,11 @@
31193 appendText(&sQuery, "SELECT * FROM ", 0);
31194 appendText(&sQuery, zTab, 0);
31195 appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
31196 }
31197 appendText(&sSql, zSep, 0);
31198 appendText(&sSql, sQuery.zTxt, '\'');
31199 sQuery.n = 0;
31200 appendText(&sSql, ",", 0);
31201 appendText(&sSql, zTab, '\'');
31202 zSep = "),(";
31203 }
@@ -31407,17 +31205,17 @@
31205 if( bSeparate ){
31206 zSql = sqlite3_mprintf(
31207 "%s))"
31208 " SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label"
31209 " FROM [sha3sum$query]",
31210 sSql.zTxt, iSize);
31211 }else{
31212 zSql = sqlite3_mprintf(
31213 "%s))"
31214 " SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash"
31215 " FROM [sha3sum$query]",
31216 sSql.zTxt, iSize);
31217 }
31218 shell_check_oom(zSql);
31219 freeText(&sQuery);
31220 freeText(&sSql);
31221 if( bDebug ){
@@ -31613,11 +31411,11 @@
31411 goto meta_command_exit;
31412 }
31413 for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
31414 const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
31415 if( zDbName==0 ) continue;
31416 if( s.zTxt && s.zTxt[0] ) appendText(&s, " UNION ALL ", 0);
31417 if( sqlite3_stricmp(zDbName, "main")==0 ){
31418 appendText(&s, "SELECT name FROM ", 0);
31419 }else{
31420 appendText(&s, "SELECT ", 0);
31421 appendText(&s, zDbName, '\'');
@@ -31635,11 +31433,11 @@
31433 }
31434 }
31435 rc = sqlite3_finalize(pStmt);
31436 if( rc==SQLITE_OK ){
31437 appendText(&s, " ORDER BY 1", 0);
31438 rc = sqlite3_prepare_v2(p->db, s.zTxt, -1, &pStmt, 0);
31439 }
31440 freeText(&s);
31441 if( rc ) return shellDatabaseError(p->db);
31442
31443 /* Run the SQL statement prepared by the above block. Store the results
@@ -31652,14 +31450,14 @@
31450 sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
31451 }
31452 while( sqlite3_step(pStmt)==SQLITE_ROW ){
31453 if( nRow>=nAlloc ){
31454 char **azNew;
31455 sqlite3_int64 n2 = 2*(sqlite3_int64)nAlloc + 10;
31456 azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
31457 shell_check_oom(azNew);
31458 nAlloc = (int)n2;
31459 azResult = azNew;
31460 }
31461 azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
31462 shell_check_oom(azResult[nRow]);
31463 nRow++;
@@ -31818,11 +31616,11 @@
31616 { 0x00000020, 1, "CoverIdxScan" },
31617 { 0x00000040, 1, "OrderByIdxJoin" },
31618 { 0x00000080, 1, "Transitive" },
31619 { 0x00000100, 1, "OmitNoopJoin" },
31620 { 0x00000200, 1, "CountOfView" },
31621 { 0x00000400, 1, "CursorHints" },
31622 { 0x00000800, 1, "Stat4" },
31623 { 0x00001000, 1, "PushDown" },
31624 { 0x00002000, 1, "SimplifyJoin" },
31625 { 0x00004000, 1, "SkipScan" },
31626 { 0x00008000, 1, "PropagateConst" },
@@ -32365,11 +32163,14 @@
32163 p->nWidth = nArg-1;
32164 p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
32165 if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
32166 if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
32167 for(j=1; j<nArg; j++){
32168 i64 w = integerValue(azArg[j]);
32169 if( w < -30000 ) w = -30000;
32170 if( w > +30000 ) w = +30000;
32171 p->colWidth[j-1] = (int)w;
32172 }
32173 }else
32174
32175 {
32176 sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: "
@@ -32888,76 +32689,95 @@
32689
32690 return home_dir;
32691 }
32692
32693 /*
32694 ** On non-Windows platforms, look for:
32695 **
32696 ** - ${zEnvVar}/${zBaseName}
32697 ** - ${HOME}/${zSubdir}/${zBaseName}
32698 **
32699 ** $zEnvVar is intended to be the name of an XDG_... environment
32700 ** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
32701 ** NULL or getenv(zEnvVar) is NULL then fall back to the second
32702 ** option. If the selected option is not found in the filesystem,
32703 ** return 0.
32704 **
32705 ** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
32706 ** becomes the fallback.
32707 **
32708 ** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
32709 ** will conventionally be ".config" or ".local/state", which, not
32710 ** coincidentally, is the typical subdir of the corresponding XDG_...
32711 ** var with the XDG var's $HOME prefix.
32712 **
32713 ** The returned string is obtained from sqlite3_malloc() and should be
32714 ** sqlite3_free()'d by the caller.
32715 */
32716 static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
32717 const char *zBaseName){
32718 #if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
32719 || defined(__RTP__) || defined(_WRS_KERNEL)
32720 return 0;
32721 #else
32722 char *zConfigFile = 0;
32723 const char *zXdgDir;
32724
32725 zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
32726 if( zXdgDir ){
32727 zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
 
 
32728 }else{
32729 const char * zHome = find_home_dir(0);
32730 if( zHome==0 ) return 0;
32731 zConfigFile = (zSubdir && *zSubdir)
32732 ? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
32733 : sqlite3_mprintf("%s/%s", zHome, zBaseName);
32734 }
32735 shell_check_oom(zConfigFile);
32736 if( access(zConfigFile,0)!=0 ){
32737 sqlite3_free(zConfigFile);
32738 zConfigFile = 0;
32739 }
32740 return zConfigFile;
32741 #endif
32742 }
32743
32744 /*
32745 ** Read input from the file sqliterc_override. If that parameter is
32746 ** NULL, take it from find_xdg_file(), if found, or fall back to
32747 ** ~/.sqliterc.
32748 **
32749 ** Failure to read the config is only considered a failure if
32750 ** sqliterc_override is not NULL, in which case this function may emit
32751 ** a warning or, if ::bail_on_error is true, fail fatally if the file
32752 ** named by sqliterc_override is not found.
32753 */
32754 static void process_sqliterc(
32755 ShellState *p, /* Configuration data */
32756 const char *sqliterc_override /* Name of config file. NULL to use default */
32757 ){
32758 char *home_dir = NULL;
32759 char *sqliterc = (char*)sqliterc_override;
 
32760 FILE *inSaved = p->in;
32761 int savedLineno = p->lineno;
32762
32763 if( sqliterc == NULL ){
32764 sqliterc = find_xdg_file("XDG_CONFIG_HOME",
32765 ".config",
32766 "sqlite3/sqliterc");
32767 }
32768 if( sqliterc == NULL ){
32769 home_dir = find_home_dir(0);
32770 if( home_dir==0 ){
32771 eputz("-- warning: cannot find home directory;"
32772 " cannot read ~/.sqliterc\n");
32773 return;
32774 }
32775 sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
32776 shell_check_oom(sqliterc);
 
32777 }
32778 p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
32779 if( p->in ){
32780 if( stdin_is_interactive ){
32781 sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
32782 }
32783 if( process_input(p) && bail_on_error ) exit(1);
@@ -32966,11 +32786,13 @@
32786 sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
32787 if( bail_on_error ) exit(1);
32788 }
32789 p->in = inSaved;
32790 p->lineno = savedLineno;
32791 if( sqliterc != sqliterc_override ){
32792 sqlite3_free(sqliterc);
32793 }
32794 }
32795
32796 /*
32797 ** Show available command line options
32798 */
@@ -32997,10 +32819,11 @@
32819 #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
32820 " -heap SIZE Size of heap for memsys3 or memsys5\n"
32821 #endif
32822 " -help show this message\n"
32823 " -html set output mode to HTML\n"
32824 " -ifexists only open if database already exists\n"
32825 " -interactive force interactive I/O\n"
32826 " -json set output mode to 'json'\n"
32827 " -line set output mode to 'line'\n"
32828 " -list set output mode to 'list'\n"
32829 " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
@@ -33341,16 +33164,24 @@
33164 (void)cmdline_option_value(argc, argv, ++i);
33165 #endif
33166 }else if( cli_strcmp(z,"-pagecache")==0 ){
33167 sqlite3_int64 n, sz;
33168 sz = integerValue(cmdline_option_value(argc,argv,++i));
33169 if( sz>65536 ) sz = 65536;
33170 if( sz<0 ) sz = 0;
33171 n = integerValue(cmdline_option_value(argc,argv,++i));
33172 if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
33173 n = 0xffffffffffffLL/sz;
33174 }
33175 if( sz>0 && (sz & (sz-1))==0 ){
33176 /* If SIZE is a power of two, round it up by the PCACHE_HDRSZ */
33177 int szHdr = 0;
33178 sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &szHdr);
33179 sz += szHdr;
33180 sqlite3_fprintf(stdout, "Page cache size increased to %d to accommodate"
33181 " the %d-byte headers\n", (int)sz, szHdr);
33182 }
33183 verify_uninitialized();
33184 sqlite3_config(SQLITE_CONFIG_PAGECACHE,
33185 (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
33186 data.shellFlgs |= SHFLG_Pagecache;
33187 }else if( cli_strcmp(z,"-lookaside")==0 ){
@@ -33359,11 +33190,11 @@
33190 if( sz<0 ) sz = 0;
33191 n = (int)integerValue(cmdline_option_value(argc,argv,++i));
33192 if( n<0 ) n = 0;
33193 verify_uninitialized();
33194 sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
33195 if( (i64)sz*(i64)n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
33196 }else if( cli_strcmp(z,"-threadsafe")==0 ){
33197 int n;
33198 n = (int)integerValue(cmdline_option_value(argc,argv,++i));
33199 verify_uninitialized();
33200 switch( n ){
@@ -33401,13 +33232,19 @@
33232 data.openMode = SHELL_OPEN_DESERIALIZE;
33233 }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
33234 data.szMax = integerValue(argv[++i]);
33235 #endif
33236 }else if( cli_strcmp(z,"-readonly")==0 ){
33237 data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
33238 data.openFlags |= SQLITE_OPEN_READONLY;
33239 }else if( cli_strcmp(z,"-nofollow")==0 ){
33240 data.openFlags |= SQLITE_OPEN_NOFOLLOW;
33241 }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
33242 data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
33243 }else if( cli_strcmp(z,"-ifexists")==0 ){
33244 data.openFlags &= ~(SQLITE_OPEN_CREATE);
33245 if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
33246 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
33247 }else if( cli_strncmp(z, "-A",2)==0 ){
33248 /* All remaining command-line arguments are passed to the ".archive"
33249 ** command, so ignore them */
33250 break;
@@ -33557,13 +33394,19 @@
33394 data.openMode = SHELL_OPEN_DESERIALIZE;
33395 }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
33396 data.szMax = integerValue(argv[++i]);
33397 #endif
33398 }else if( cli_strcmp(z,"-readonly")==0 ){
33399 data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
33400 data.openFlags |= SQLITE_OPEN_READONLY;
33401 }else if( cli_strcmp(z,"-nofollow")==0 ){
33402 data.openFlags |= SQLITE_OPEN_NOFOLLOW;
33403 }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
33404 data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
33405 }else if( cli_strcmp(z,"-ifexists")==0 ){
33406 data.openFlags &= ~(SQLITE_OPEN_CREATE);
33407 if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
33408 }else if( cli_strcmp(z,"-ascii")==0 ){
33409 data.mode = MODE_Ascii;
33410 sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit);
33411 sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record);
33412 }else if( cli_strcmp(z,"-tabs")==0 ){
@@ -33734,11 +33577,10 @@
33577 /* Run commands received from standard input
33578 */
33579 if( stdin_is_interactive ){
33580 char *zHome;
33581 char *zHistory;
 
33582 sqlite3_fprintf(stdout,
33583 "SQLite version %s %.19s\n" /*extra-version-info*/
33584 "Enter \".help\" for usage hints.\n",
33585 sqlite3_libversion(), sqlite3_sourceid());
33586 if( warnInmemoryDb ){
@@ -33747,15 +33589,19 @@
33589 sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
33590 " persistent database.\n");
33591 }
33592 zHistory = getenv("SQLITE_HISTORY");
33593 if( zHistory ){
33594 zHistory = sqlite3_mprintf("%s", zHistory);
33595 shell_check_oom(zHistory);
33596 }else{
33597 zHistory = find_xdg_file("XDG_STATE_HOME",
33598 ".local/state",
33599 "sqlite_history");
33600 if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
33601 zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
33602 shell_check_oom(zHistory);
33603 }
33604 }
33605 if( zHistory ){ shell_read_history(zHistory); }
33606 #if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
33607 rl_attempted_completion_function = readline_completion;
@@ -33767,21 +33613,21 @@
33613 data.in = 0;
33614 rc = process_input(&data);
33615 if( zHistory ){
33616 shell_stifle_history(2000);
33617 shell_write_history(zHistory);
33618 sqlite3_free(zHistory);
33619 }
33620 }else{
33621 data.in = stdin;
33622 rc = process_input(&data);
33623 }
33624 }
33625 #ifndef SQLITE_SHELL_FIDDLE
33626 /* In WASM mode we have to leave the db state in place so that
33627 ** client code can "push" SQL into it after this call returns. */
33628 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
33629 if( data.expert.pExpert ){
33630 expertFinish(&data, 1, 0);
33631 }
33632 #endif
33633 shell_main_exit:
33634
+2521 -708
--- 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
-** 9f184f8dfa5ef6d57e10376adc30e0060ced with changes in files:
21
+** 5cbccab499bc3983aac1f57355552db607de with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -168,11 +168,13 @@
168168
#define SQLITE_OMIT_LOAD_EXTENSION 1
169169
#define SQLITE_ENABLE_LOCKING_STYLE 0
170170
#define HAVE_UTIME 1
171171
#else
172172
/* This is not VxWorks. */
173
-#define OS_VXWORKS 0
173
+#ifndef OS_VXWORKS
174
+# define OS_VXWORKS 0
175
+#endif
174176
#define HAVE_FCHOWN 1
175177
#define HAVE_READLINK 1
176178
#define HAVE_LSTAT 1
177179
#endif /* defined(_WRS_KERNEL) */
178180
@@ -465,11 +467,14 @@
465467
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466468
** [sqlite_version()] and [sqlite_source_id()].
467469
*/
468470
#define SQLITE_VERSION "3.51.0"
469471
#define SQLITE_VERSION_NUMBER 3051000
470
-#define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
472
+#define SQLITE_SOURCE_ID "2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70"
473
+#define SQLITE_SCM_BRANCH "trunk"
474
+#define SQLITE_SCM_TAGS ""
475
+#define SQLITE_SCM_DATETIME "2025-10-15T10:52:45.276Z"
471476
472477
/*
473478
** CAPI3REF: Run-Time Library Version Numbers
474479
** KEYWORDS: sqlite3_version sqlite3_sourceid
475480
**
@@ -814,10 +819,13 @@
814819
** [sqlite3_extended_errcode()].
815820
*/
816821
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
817822
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
818823
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
824
+#define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
825
+#define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
826
+#define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
819827
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
820828
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
821829
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
822830
#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
823831
#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -848,10 +856,12 @@
848856
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
849857
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
850858
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
851859
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
852860
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
861
+#define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
862
+#define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
853863
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
854864
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
855865
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
856866
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
857867
#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -2652,21 +2662,24 @@
26522662
** views in the main database schema or in the schemas of ATTACH-ed
26532663
** databases.)^ </dd>
26542664
**
26552665
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
26562666
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2657
-** <dd> ^This option is used to enable or disable the
2658
-** [fts3_tokenizer()] function which is part of the
2659
-** [FTS3] full-text search engine extension.
2660
-** There must be two additional arguments.
2661
-** The first argument is an integer which is 0 to disable fts3_tokenizer() or
2662
-** positive to enable fts3_tokenizer() or negative to leave the setting
2663
-** unchanged.
2664
-** The second parameter is a pointer to an integer into which
2665
-** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
2666
-** following this call. The second parameter may be a NULL pointer, in
2667
-** which case the new setting is not reported back. </dd>
2667
+** <dd> ^This option is used to enable or disable using the
2668
+** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine
2669
+** extension - without using bound parameters as the parameters. Doing so
2670
+** is disabled by default. There must be two additional arguments. The first
2671
+** argument is an integer. If it is passed 0, then using fts3_tokenizer()
2672
+** without bound parameters is disabled. If it is passed a positive value,
2673
+** then calling fts3_tokenizer without bound parameters is enabled. If it
2674
+** is passed a negative value, this setting is not modified - this can be
2675
+** used to query for the current setting. The second parameter is a pointer
2676
+** to an integer into which is written 0 or 1 to indicate the current value
2677
+** of this setting (after it is modified, if applicable). The second
2678
+** parameter may be a NULL pointer, in which case the value of the setting
2679
+** is not reported back. Refer to [FTS3] documentation for further details.
2680
+** </dd>
26682681
**
26692682
** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
26702683
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
26712684
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
26722685
** interface independently of the [load_extension()] SQL function.
@@ -4512,10 +4525,38 @@
45124525
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
45134526
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
45144527
SQLITE_API const char *sqlite3_errstr(int);
45154528
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
45164529
4530
+/*
4531
+** CAPI3REF: Set Error Codes And Message
4532
+** METHOD: sqlite3
4533
+**
4534
+** Set the error code of the database handle passed as the first argument
4535
+** to errcode, and the error message to a copy of nul-terminated string
4536
+** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
4537
+** the default message associated with the supplied error code. Subsequent
4538
+** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
4539
+** return the values set by this routine in place of what was previously
4540
+** set by SQLite itself.
4541
+**
4542
+** This function returns SQLITE_OK if the error code and error message are
4543
+** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
4544
+** the database handle is NULL or invalid.
4545
+**
4546
+** The error code and message set by this routine remains in effect until
4547
+** they are changed, either by another call to this routine or until they are
4548
+** changed to by SQLite itself to reflect the result of some subsquent
4549
+** API call.
4550
+**
4551
+** This function is intended for use by SQLite extensions or wrappers. The
4552
+** idea is that an extension or wrapper can use this routine to set error
4553
+** messages and error codes and thus behave more like a core SQLite
4554
+** feature from the point of view of an application.
4555
+*/
4556
+SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
4557
+
45174558
/*
45184559
** CAPI3REF: Prepared Statement Object
45194560
** KEYWORDS: {prepared statement} {prepared statements}
45204561
**
45214562
** An instance of this object represents a single SQL statement that
@@ -6522,10 +6563,11 @@
65226563
** to be attached to [database connection] D using name N. Subsequent
65236564
** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
65246565
** or a NULL pointer if there were no prior calls to
65256566
** sqlite3_set_clientdata() with the same values of D and N.
65266567
** Names are compared using strcmp() and are thus case sensitive.
6568
+** It returns 0 on success and SQLITE_NOMEM on allocation failure.
65276569
**
65286570
** If P and X are both non-NULL, then the destructor X is invoked with
65296571
** argument P on the first of the following occurrences:
65306572
** <ul>
65316573
** <li> An out-of-memory error occurs during the call to
@@ -9197,14 +9239,23 @@
91979239
** the resetFlg is true, then the highest instantaneous value is
91989240
** reset back down to the current value.
91999241
**
92009242
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
92019243
** non-zero [error code] on failure.
9244
+**
9245
+** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
9246
+** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
9247
+** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
9248
+** of pointers to 32-bit integers, which allows larger status values
9249
+** to be returned. If a status value exceeds 2,147,483,647 then
9250
+** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
9251
+** will return the full value.
92029252
**
92039253
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
92049254
*/
92059255
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
9256
+SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
92069257
92079258
/*
92089259
** CAPI3REF: Status Parameters for database connections
92099260
** KEYWORDS: {SQLITE_DBSTATUS options}
92109261
**
@@ -9297,10 +9348,14 @@
92979348
** database file in rollback mode databases. Any pages written as part of
92989349
** transaction rollback or database recovery operations are not included.
92999350
** If an IO or other error occurs while writing a page to disk, the effect
93009351
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
93019352
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
9353
+** <p>
9354
+** ^(There is overlap between the quantities measured by this parameter
9355
+** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL.
9356
+** Resetting one will reduce the other.)^
93029357
** </dd>
93039358
**
93049359
** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
93059360
** <dd>This parameter returns the number of dirty cache entries that have
93069361
** been written to disk in the middle of a transaction due to the page
@@ -9312,10 +9367,22 @@
93129367
**
93139368
** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
93149369
** <dd>This parameter returns zero for the current value if and only if
93159370
** all foreign key constraints (deferred or immediate) have been
93169371
** resolved.)^ ^The highwater mark is always 0.
9372
+**
9373
+** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt>
9374
+** <dd>^(This parameter returns the number of bytes written to temporary
9375
+** files on disk that could have been kept in memory had sufficient memory
9376
+** been available. This value includes writes to intermediate tables that
9377
+** are part of complex queries, external sorts that spill to disk, and
9378
+** writes to TEMP tables.)^
9379
+** ^The highwater mark is always 0.
9380
+** <p>
9381
+** ^(There is overlap between the quantities measured by this parameter
9382
+** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE.
9383
+** Resetting one will reduce the other.)^
93179384
** </dd>
93189385
** </dl>
93199386
*/
93209387
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
93219388
#define SQLITE_DBSTATUS_CACHE_USED 1
@@ -9328,11 +9395,12 @@
93289395
#define SQLITE_DBSTATUS_CACHE_MISS 8
93299396
#define SQLITE_DBSTATUS_CACHE_WRITE 9
93309397
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
93319398
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
93329399
#define SQLITE_DBSTATUS_CACHE_SPILL 12
9333
-#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
9400
+#define SQLITE_DBSTATUS_TEMPBUF_SPILL 13
9401
+#define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */
93349402
93359403
93369404
/*
93379405
** CAPI3REF: Prepared Statement Status
93389406
** METHOD: sqlite3_stmt
@@ -10093,25 +10161,38 @@
1009310161
** ^The third parameter is the name of the database that was written to -
1009410162
** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
1009510163
** is the number of pages currently in the write-ahead log file,
1009610164
** including those that were just committed.
1009710165
**
10098
-** The callback function should normally return [SQLITE_OK]. ^If an error
10166
+** ^The callback function should normally return [SQLITE_OK]. ^If an error
1009910167
** code is returned, that error will propagate back up through the
1010010168
** SQLite code base to cause the statement that provoked the callback
1010110169
** to report an error, though the commit will have still occurred. If the
1010210170
** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
1010310171
** that does not correspond to any valid SQLite error code, the results
1010410172
** are undefined.
1010510173
**
10106
-** A single database handle may have at most a single write-ahead log callback
10107
-** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
10108
-** previously registered write-ahead log callback. ^The return value is
10109
-** a copy of the third parameter from the previous call, if any, or 0.
10110
-** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
10111
-** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
10112
-** overwrite any prior [sqlite3_wal_hook()] settings.
10174
+** ^A single database handle may have at most a single write-ahead log
10175
+** callback registered at one time. ^Calling [sqlite3_wal_hook()]
10176
+** replaces the default behavior or previously registered write-ahead
10177
+** log callback.
10178
+**
10179
+** ^The return value is a copy of the third parameter from the
10180
+** previous call, if any, or 0.
10181
+**
10182
+** ^The [sqlite3_wal_autocheckpoint()] interface and the
10183
+** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and
10184
+** will overwrite any prior [sqlite3_wal_hook()] settings.
10185
+**
10186
+** ^If a write-ahead log callback is set using this function then
10187
+** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint]
10188
+** should be invoked periodically to keep the write-ahead log file
10189
+** from growing without bound.
10190
+**
10191
+** ^Passing a NULL pointer for the callback disables automatic
10192
+** checkpointing entirely. To re-enable the default behavior, call
10193
+** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint].
1011310194
*/
1011410195
SQLITE_API void *sqlite3_wal_hook(
1011510196
sqlite3*,
1011610197
int(*)(void *,sqlite3*,const char*,int),
1011710198
void*
@@ -10124,11 +10205,11 @@
1012410205
** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
1012510206
** [sqlite3_wal_hook()] that causes any database on [database connection] D
1012610207
** to automatically [checkpoint]
1012710208
** after committing a transaction if there are N or
1012810209
** more frames in the [write-ahead log] file. ^Passing zero or
10129
-** a negative value as the nFrame parameter disables automatic
10210
+** a negative value as the N parameter disables automatic
1013010211
** checkpoints entirely.
1013110212
**
1013210213
** ^The callback registered by this function replaces any existing callback
1013310214
** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback
1013410215
** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
@@ -10140,13 +10221,14 @@
1014010221
** ^Checkpoints initiated by this mechanism are
1014110222
** [sqlite3_wal_checkpoint_v2|PASSIVE].
1014210223
**
1014310224
** ^Every new [database connection] defaults to having the auto-checkpoint
1014410225
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
10145
-** pages. The use of this interface
10146
-** is only necessary if the default setting is found to be suboptimal
10147
-** for a particular application.
10226
+** pages.
10227
+**
10228
+** ^The use of this interface is only necessary if the default setting
10229
+** is found to be suboptimal for a particular application.
1014810230
*/
1014910231
SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
1015010232
1015110233
/*
1015210234
** CAPI3REF: Checkpoint a database
@@ -10207,10 +10289,15 @@
1020710289
**
1020810290
** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
1020910291
** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
1021010292
** addition that it also truncates the log file to zero bytes just prior
1021110293
** to a successful return.
10294
+**
10295
+** <dt>SQLITE_CHECKPOINT_NOOP<dd>
10296
+** ^This mode always checkpoints zero frames. The only reason to invoke
10297
+** a NOOP checkpoint is to access the values returned by
10298
+** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt.
1021210299
** </dl>
1021310300
**
1021410301
** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
1021510302
** the log file or to -1 if the checkpoint could not run because
1021610303
** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
@@ -10277,10 +10364,11 @@
1027710364
** These constants define all valid values for the "checkpoint mode" passed
1027810365
** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
1027910366
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
1028010367
** meaning of each of these checkpoint modes.
1028110368
*/
10369
+#define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
1028210370
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
1028310371
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
1028410372
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
1028510373
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
1028610374
@@ -11104,11 +11192,11 @@
1110411192
** to avoid a memory leak.
1110511193
**
1110611194
** The [sqlite3_snapshot_get()] interface is only available when the
1110711195
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
1110811196
*/
11109
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
11197
+SQLITE_API int sqlite3_snapshot_get(
1111011198
sqlite3 *db,
1111111199
const char *zSchema,
1111211200
sqlite3_snapshot **ppSnapshot
1111311201
);
1111411202
@@ -11153,11 +11241,11 @@
1115311241
** database connection in order to make it ready to use snapshots.)
1115411242
**
1115511243
** The [sqlite3_snapshot_open()] interface is only available when the
1115611244
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
1115711245
*/
11158
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
11246
+SQLITE_API int sqlite3_snapshot_open(
1115911247
sqlite3 *db,
1116011248
const char *zSchema,
1116111249
sqlite3_snapshot *pSnapshot
1116211250
);
1116311251
@@ -11170,11 +11258,11 @@
1117011258
** using this routine to avoid a memory leak.
1117111259
**
1117211260
** The [sqlite3_snapshot_free()] interface is only available when the
1117311261
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
1117411262
*/
11175
-SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
11263
+SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*);
1117611264
1117711265
/*
1117811266
** CAPI3REF: Compare the ages of two snapshot handles.
1117911267
** METHOD: sqlite3_snapshot
1118011268
**
@@ -11197,11 +11285,11 @@
1119711285
** snapshot, and a positive value if P1 is a newer snapshot than P2.
1119811286
**
1119911287
** This interface is only available if SQLite is compiled with the
1120011288
** [SQLITE_ENABLE_SNAPSHOT] option.
1120111289
*/
11202
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
11290
+SQLITE_API int sqlite3_snapshot_cmp(
1120311291
sqlite3_snapshot *p1,
1120411292
sqlite3_snapshot *p2
1120511293
);
1120611294
1120711295
/*
@@ -11225,11 +11313,11 @@
1122511313
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
1122611314
**
1122711315
** This interface is only available if SQLite is compiled with the
1122811316
** [SQLITE_ENABLE_SNAPSHOT] option.
1122911317
*/
11230
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
11318
+SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
1123111319
1123211320
/*
1123311321
** CAPI3REF: Serialize a database
1123411322
**
1123511323
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
@@ -11299,16 +11387,17 @@
1129911387
/*
1130011388
** CAPI3REF: Deserialize a database
1130111389
**
1130211390
** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
1130311391
** [database connection] D to disconnect from database S and then
11304
-** reopen S as an in-memory database based on the serialization contained
11305
-** in P. The serialized database P is N bytes in size. M is the size of
11306
-** the buffer P, which might be larger than N. If M is larger than N, and
11307
-** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
11308
-** permitted to add content to the in-memory database as long as the total
11309
-** size does not exceed M bytes.
11392
+** reopen S as an in-memory database based on the serialization
11393
+** contained in P. If S is a NULL pointer, the main database is
11394
+** used. The serialized database P is N bytes in size. M is the size
11395
+** of the buffer P, which might be larger than N. If M is larger than
11396
+** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then
11397
+** SQLite is permitted to add content to the in-memory database as
11398
+** long as the total size does not exceed M bytes.
1131011399
**
1131111400
** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
1131211401
** invoke sqlite3_free() on the serialization buffer when the database
1131311402
** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
1131411403
** SQLite will try to increase the buffer size using sqlite3_realloc64()
@@ -11371,10 +11460,56 @@
1137111460
*/
1137211461
#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
1137311462
#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
1137411463
#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
1137511464
11465
+/*
11466
+** CAPI3REF: Bind array values to the CARRAY table-valued function
11467
+**
11468
+** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to
11469
+** one of the first argument of the [carray() table-valued function]. The
11470
+** S parameter is a pointer to the [prepared statement] that uses the carray()
11471
+** functions. I is the parameter index to be bound. P is a pointer to the
11472
+** array to be bound, and N is the number of eements in the array. The
11473
+** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
11474
+** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to
11475
+** indicate the datatype of the array being bound. The X argument is not a
11476
+** NULL pointer, then SQLite will invoke the function X on the P parameter
11477
+** after it has finished using P.
11478
+*/
11479
+SQLITE_API SQLITE_API int sqlite3_carray_bind(
11480
+ sqlite3_stmt *pStmt, /* Statement to be bound */
11481
+ int i, /* Parameter index */
11482
+ void *aData, /* Pointer to array data */
11483
+ int nData, /* Number of data elements */
11484
+ int mFlags, /* CARRAY flags */
11485
+ void (*xDel)(void*) /* Destructor for aData */
11486
+);
11487
+
11488
+/*
11489
+** CAPI3REF: Datatypes for the CARRAY table-valued funtion
11490
+**
11491
+** The fifth argument to the [sqlite3_carray_bind()] interface musts be
11492
+** one of the following constants, to specify the datatype of the array
11493
+** that is being bound into the [carray table-valued function].
11494
+*/
11495
+#define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */
11496
+#define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */
11497
+#define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */
11498
+#define SQLITE_CARRAY_TEXT 3 /* Data is char* */
11499
+#define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */
11500
+
11501
+/*
11502
+** Versions of the above #defines that omit the initial SQLITE_, for
11503
+** legacy compatibility.
11504
+*/
11505
+#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
11506
+#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
11507
+#define CARRAY_DOUBLE 2 /* Data is doubles */
11508
+#define CARRAY_TEXT 3 /* Data is char* */
11509
+#define CARRAY_BLOB 4 /* Data is struct iovec */
11510
+
1137611511
/*
1137711512
** Undo the hack that converts floating point types to integer for
1137811513
** builds on processors without floating point support.
1137911514
*/
1138011515
#ifdef SQLITE_OMIT_FLOATING_POINT
@@ -12629,10 +12764,19 @@
1262912764
** CAPI3REF: Apply A Changeset To A Database
1263012765
**
1263112766
** Apply a changeset or patchset to a database. These functions attempt to
1263212767
** update the "main" database attached to handle db with the changes found in
1263312768
** the changeset passed via the second and third arguments.
12769
+**
12770
+** All changes made by these functions are enclosed in a savepoint transaction.
12771
+** If any other error (aside from a constraint failure when attempting to
12772
+** write to the target database) occurs, then the savepoint transaction is
12773
+** rolled back, restoring the target database to its original state, and an
12774
+** SQLite error code returned. Additionally, starting with version 3.51.0,
12775
+** an error code and error message that may be accessed using the
12776
+** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database
12777
+** handle.
1263412778
**
1263512779
** The fourth argument (xFilter) passed to these functions is the "filter
1263612780
** callback". This may be passed NULL, in which case all changes in the
1263712781
** changeset are applied to the database. For sqlite3changeset_apply() and
1263812782
** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
@@ -12767,16 +12911,10 @@
1276712911
** It is safe to execute SQL statements, including those that write to the
1276812912
** table that the callback related to, from within the xConflict callback.
1276912913
** This can be used to further customize the application's conflict
1277012914
** resolution strategy.
1277112915
**
12772
-** All changes made by these functions are enclosed in a savepoint transaction.
12773
-** If any other error (aside from a constraint failure when attempting to
12774
-** write to the target database) occurs, then the savepoint transaction is
12775
-** rolled back, restoring the target database to its original state, and an
12776
-** SQLite error code returned.
12777
-**
1277812916
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
1277912917
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
1278012918
** may set (*ppRebase) to point to a "rebase" that may be used with the
1278112919
** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
1278212920
** is set to the size of the buffer in bytes. It is the responsibility of the
@@ -14351,11 +14489,11 @@
1435114489
1435214490
/*
1435314491
** Maximum number of pages in one database file.
1435414492
**
1435514493
** This is really just the default value for the max_page_count pragma.
14356
-** This value can be lowered (or raised) at run-time using that the
14494
+** This value can be lowered (or raised) at run-time using the
1435714495
** max_page_count macro.
1435814496
*/
1435914497
#ifndef SQLITE_MAX_PAGE_COUNT
1436014498
# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
1436114499
#endif
@@ -15947,12 +16085,12 @@
1594716085
**
1594816086
** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application
1594916087
** must provide its own VFS implementation together with sqlite3_os_init()
1595016088
** and sqlite3_os_end() routines.
1595116089
*/
15952
-#if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \
15953
- !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN)
16090
+#if SQLITE_OS_KV+1<=1 && SQLITE_OS_OTHER+1<=1 && \
16091
+ SQLITE_OS_WIN+1<=1 && SQLITE_OS_UNIX+1<=1
1595416092
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
1595516093
defined(__MINGW32__) || defined(__BORLANDC__)
1595616094
# define SQLITE_OS_WIN 1
1595716095
# define SQLITE_OS_UNIX 0
1595816096
# else
@@ -17450,10 +17588,13 @@
1745017588
#ifndef SQLITE_OMIT_TRACE
1745117589
SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
1745217590
#endif
1745317591
SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
1745417592
SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*);
17593
+#ifdef SQLITE_ENABLE_PERCENTILE
17594
+SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context*);
17595
+#endif
1745517596
1745617597
SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*);
1745717598
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
1745817599
SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
1745917600
SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
@@ -18122,11 +18263,11 @@
1812218263
struct sqlite3InitInfo { /* Information used during initialization */
1812318264
Pgno newTnum; /* Rootpage of table being initialized */
1812418265
u8 iDb; /* Which db file is being initialized */
1812518266
u8 busy; /* TRUE if currently initializing */
1812618267
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
18127
- unsigned imposterTable : 1; /* Building an imposter table */
18268
+ unsigned imposterTable : 2; /* Building an imposter table */
1812818269
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
1812918270
const char **azInit; /* "type", "name", and "tbl_name" columns */
1813018271
} init;
1813118272
int nVdbeActive; /* Number of VDBEs currently running */
1813218273
int nVdbeRead; /* Number of active VDBEs that read or write */
@@ -18205,10 +18346,11 @@
1820518346
int nStatement; /* Number of nested statement-transactions */
1820618347
i64 nDeferredCons; /* Net deferred constraints this transaction. */
1820718348
i64 nDeferredImmCons; /* Net deferred immediate constraints */
1820818349
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
1820918350
DbClientData *pDbData; /* sqlite3_set_clientdata() content */
18351
+ u64 nSpill; /* TEMP content spilled to disk */
1821018352
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
1821118353
/* The following variables are all protected by the STATIC_MAIN
1821218354
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
1821318355
**
1821418356
** When X.pUnlockConnection==Y, that means that X is waiting for Y to
@@ -18915,10 +19057,11 @@
1891519057
#define TF_Shadow 0x00001000 /* True for a shadow table */
1891619058
#define TF_HasStat4 0x00002000 /* STAT4 info available for this table */
1891719059
#define TF_Ephemeral 0x00004000 /* An ephemeral table */
1891819060
#define TF_Eponymous 0x00008000 /* An eponymous virtual table */
1891919061
#define TF_Strict 0x00010000 /* STRICT mode */
19062
+#define TF_Imposter 0x00020000 /* An imposter table */
1892019063
1892119064
/*
1892219065
** Allowed values for Table.eTabType
1892319066
*/
1892419067
#define TABTYP_NORM 0 /* Ordinary table */
@@ -19503,10 +19646,11 @@
1950319646
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
1950419647
union {
1950519648
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
1950619649
** for a column of an index on an expression */
1950719650
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
19651
+ int nReg; /* TK_NULLS: Number of registers to NULL out */
1950819652
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
1950919653
int iAddr; /* Subroutine entry address */
1951019654
int regReturn; /* Register used to hold return address */
1951119655
} sub;
1951219656
} y;
@@ -20080,10 +20224,11 @@
2008020224
#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
2008120225
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
2008220226
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
2008320227
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
2008420228
#define SF_Correlated 0x20000000 /* True if references the outer context */
20229
+#define SF_OnToWhere 0x40000000 /* One or more ON clauses moved to WHERE */
2008520230
2008620231
/* True if SrcItem X is a subquery that has SF_NestedFrom */
2008720232
#define IsNestedFrom(X) \
2008820233
((X)->fg.isSubquery && \
2008920234
((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0)
@@ -20833,10 +20978,11 @@
2083320978
struct Table *pTab; /* Table of generated column */
2083420979
struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
2083520980
SrcItem *pSrcItem; /* A single FROM clause item */
2083620981
DbFixer *pFix; /* See sqlite3FixSelect() */
2083720982
Mem *aMem; /* See sqlite3BtreeCursorHint() */
20983
+ struct CheckOnCtx *pCheckOnCtx; /* See selectCheckOnClauses() */
2083820984
} u;
2083920985
};
2084020986
2084120987
/*
2084220988
** The following structure contains information used by the sqliteFix...
@@ -21540,10 +21686,11 @@
2154021686
SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
2154121687
#endif
2154221688
SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
2154321689
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
2154421690
SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
21691
+SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int);
2154521692
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
2154621693
SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
2154721694
SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
2154821695
#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
2154921696
#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -21636,16 +21783,20 @@
2163621783
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
2163721784
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
2163821785
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
2163921786
SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
2164021787
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
21641
-SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3*);
21788
+SQLITE_PRIVATE Module *sqlite3JsonVtabRegister(sqlite3*,const char*);
2164221789
#endif
2164321790
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
2164421791
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
2164521792
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
2164621793
SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p);
21794
+
21795
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY)
21796
+SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3*);
21797
+#endif
2164721798
2164821799
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
2164921800
SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
2165021801
#endif
2165121802
@@ -22628,10 +22779,13 @@
2262822779
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
2262922780
"ENABLE_BATCH_ATOMIC_WRITE",
2263022781
#endif
2263122782
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
2263222783
"ENABLE_BYTECODE_VTAB",
22784
+#endif
22785
+#ifdef SQLITE_ENABLE_CARRAY
22786
+ "ENABLE_CARRAY",
2263322787
#endif
2263422788
#ifdef SQLITE_ENABLE_CEROD
2263522789
"ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
2263622790
#endif
2263722791
#ifdef SQLITE_ENABLE_COLUMN_METADATA
@@ -22718,10 +22872,13 @@
2271822872
#ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
2271922873
"ENABLE_ORDERED_SET_AGGREGATES",
2272022874
#endif
2272122875
#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
2272222876
"ENABLE_OVERSIZE_CELL_CHECK",
22877
+#endif
22878
+#ifdef SQLITE_ENABLE_PERCENTILE
22879
+ "ENABLE_PERCENTILE",
2272322880
#endif
2272422881
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
2272522882
"ENABLE_PREUPDATE_HOOK",
2272622883
#endif
2272722884
#ifdef SQLITE_ENABLE_QPSG
@@ -24202,11 +24359,14 @@
2420224359
Mem oldipk; /* Memory cell holding "old" IPK value */
2420324360
Mem *aNew; /* Array of new.* values */
2420424361
Table *pTab; /* Schema object being updated */
2420524362
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
2420624363
sqlite3_value **apDflt; /* Array of default values, if required */
24207
- u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
24364
+ union {
24365
+ KeyInfo sKey;
24366
+ u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
24367
+ } uKey;
2420824368
};
2420924369
2421024370
/*
2421124371
** An instance of this object is used to pass an vector of values into
2421224372
** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -24366,13 +24526,15 @@
2436624526
SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
2436724527
SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
2436824528
#endif
2436924529
2437024530
#ifndef SQLITE_OMIT_FOREIGN_KEY
24371
-SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int);
24531
+SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe*);
24532
+SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe*);
2437224533
#else
24373
-# define sqlite3VdbeCheckFk(p,i) 0
24534
+# define sqlite3VdbeCheckFkImmediate(p) 0
24535
+# define sqlite3VdbeCheckFkDeferred(p) 0
2437424536
#endif
2437524537
2437624538
#ifdef SQLITE_DEBUG
2437724539
SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
2437824540
SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
@@ -24577,27 +24739,29 @@
2457724739
}
2457824740
2457924741
/*
2458024742
** Query status information for a single database connection
2458124743
*/
24582
-SQLITE_API int sqlite3_db_status(
24583
- sqlite3 *db, /* The database connection whose status is desired */
24584
- int op, /* Status verb */
24585
- int *pCurrent, /* Write current value here */
24586
- int *pHighwater, /* Write high-water mark here */
24587
- int resetFlag /* Reset high-water mark if true */
24744
+SQLITE_API int sqlite3_db_status64(
24745
+ sqlite3 *db, /* The database connection whose status is desired */
24746
+ int op, /* Status verb */
24747
+ sqlite3_int64 *pCurrent, /* Write current value here */
24748
+ sqlite3_int64 *pHighwtr, /* Write high-water mark here */
24749
+ int resetFlag /* Reset high-water mark if true */
2458824750
){
2458924751
int rc = SQLITE_OK; /* Return code */
2459024752
#ifdef SQLITE_ENABLE_API_ARMOR
24591
- if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){
24753
+ if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwtr==0 ){
2459224754
return SQLITE_MISUSE_BKPT;
2459324755
}
2459424756
#endif
2459524757
sqlite3_mutex_enter(db->mutex);
2459624758
switch( op ){
2459724759
case SQLITE_DBSTATUS_LOOKASIDE_USED: {
24598
- *pCurrent = sqlite3LookasideUsed(db, pHighwater);
24760
+ int H = 0;
24761
+ *pCurrent = sqlite3LookasideUsed(db, &H);
24762
+ *pHighwtr = H;
2459924763
if( resetFlag ){
2460024764
LookasideSlot *p = db->lookaside.pFree;
2460124765
if( p ){
2460224766
while( p->pNext ) p = p->pNext;
2460324767
p->pNext = db->lookaside.pInit;
@@ -24624,11 +24788,11 @@
2462424788
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
2462524789
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
2462624790
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
2462724791
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
2462824792
*pCurrent = 0;
24629
- *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
24793
+ *pHighwtr = db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
2463024794
if( resetFlag ){
2463124795
db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
2463224796
}
2463324797
break;
2463424798
}
@@ -24638,11 +24802,11 @@
2463824802
** by all pagers associated with the given database connection. The
2463924803
** highwater mark is meaningless and is returned as zero.
2464024804
*/
2464124805
case SQLITE_DBSTATUS_CACHE_USED_SHARED:
2464224806
case SQLITE_DBSTATUS_CACHE_USED: {
24643
- int totalUsed = 0;
24807
+ sqlite3_int64 totalUsed = 0;
2464424808
int i;
2464524809
sqlite3BtreeEnterAll(db);
2464624810
for(i=0; i<db->nDb; i++){
2464724811
Btree *pBt = db->aDb[i].pBt;
2464824812
if( pBt ){
@@ -24654,22 +24818,22 @@
2465424818
totalUsed += nByte;
2465524819
}
2465624820
}
2465724821
sqlite3BtreeLeaveAll(db);
2465824822
*pCurrent = totalUsed;
24659
- *pHighwater = 0;
24823
+ *pHighwtr = 0;
2466024824
break;
2466124825
}
2466224826
2466324827
/*
2466424828
** *pCurrent gets an accurate estimate of the amount of memory used
2466524829
** to store the schema for all databases (main, temp, and any ATTACHed
24666
- ** databases. *pHighwater is set to zero.
24830
+ ** databases. *pHighwtr is set to zero.
2466724831
*/
2466824832
case SQLITE_DBSTATUS_SCHEMA_USED: {
24669
- int i; /* Used to iterate through schemas */
24670
- int nByte = 0; /* Used to accumulate return value */
24833
+ int i; /* Used to iterate through schemas */
24834
+ int nByte = 0; /* Used to accumulate return value */
2467124835
2467224836
sqlite3BtreeEnterAll(db);
2467324837
db->pnBytesFreed = &nByte;
2467424838
assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
2467524839
db->lookaside.pEnd = db->lookaside.pStart;
@@ -24699,19 +24863,19 @@
2469924863
}
2470024864
db->pnBytesFreed = 0;
2470124865
db->lookaside.pEnd = db->lookaside.pTrueEnd;
2470224866
sqlite3BtreeLeaveAll(db);
2470324867
24704
- *pHighwater = 0;
24868
+ *pHighwtr = 0;
2470524869
*pCurrent = nByte;
2470624870
break;
2470724871
}
2470824872
2470924873
/*
2471024874
** *pCurrent gets an accurate estimate of the amount of memory used
2471124875
** to store all prepared statements.
24712
- ** *pHighwater is set to zero.
24876
+ ** *pHighwtr is set to zero.
2471324877
*/
2471424878
case SQLITE_DBSTATUS_STMT_USED: {
2471524879
struct Vdbe *pVdbe; /* Used to iterate through VMs */
2471624880
int nByte = 0; /* Used to accumulate return value */
2471724881
@@ -24722,19 +24886,19 @@
2472224886
sqlite3VdbeDelete(pVdbe);
2472324887
}
2472424888
db->lookaside.pEnd = db->lookaside.pTrueEnd;
2472524889
db->pnBytesFreed = 0;
2472624890
24727
- *pHighwater = 0; /* IMP: R-64479-57858 */
24891
+ *pHighwtr = 0; /* IMP: R-64479-57858 */
2472824892
*pCurrent = nByte;
2472924893
2473024894
break;
2473124895
}
2473224896
2473324897
/*
2473424898
** Set *pCurrent to the total cache hits or misses encountered by all
24735
- ** pagers the database handle is connected to. *pHighwater is always set
24899
+ ** pagers the database handle is connected to. *pHighwtr is always set
2473624900
** to zero.
2473724901
*/
2473824902
case SQLITE_DBSTATUS_CACHE_SPILL:
2473924903
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
2474024904
/* no break */ deliberate_fall_through
@@ -24750,23 +24914,43 @@
2475024914
if( db->aDb[i].pBt ){
2475124915
Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
2475224916
sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
2475324917
}
2475424918
}
24755
- *pHighwater = 0; /* IMP: R-42420-56072 */
24919
+ *pHighwtr = 0; /* IMP: R-42420-56072 */
2475624920
/* IMP: R-54100-20147 */
2475724921
/* IMP: R-29431-39229 */
24758
- *pCurrent = (int)nRet & 0x7fffffff;
24922
+ *pCurrent = nRet;
24923
+ break;
24924
+ }
24925
+
24926
+ /* Set *pCurrent to the number of bytes that the db database connection
24927
+ ** has spilled to the filesystem in temporary files that could have been
24928
+ ** stored in memory, had sufficient memory been available.
24929
+ ** The *pHighwater is always set to zero.
24930
+ */
24931
+ case SQLITE_DBSTATUS_TEMPBUF_SPILL: {
24932
+ u64 nRet = 0;
24933
+ if( db->aDb[1].pBt ){
24934
+ Pager *pPager = sqlite3BtreePager(db->aDb[1].pBt);
24935
+ sqlite3PagerCacheStat(pPager, SQLITE_DBSTATUS_CACHE_WRITE,
24936
+ resetFlag, &nRet);
24937
+ nRet *= sqlite3BtreeGetPageSize(db->aDb[1].pBt);
24938
+ }
24939
+ nRet += db->nSpill;
24940
+ if( resetFlag ) db->nSpill = 0;
24941
+ *pHighwtr = 0;
24942
+ *pCurrent = nRet;
2475924943
break;
2476024944
}
2476124945
2476224946
/* Set *pCurrent to non-zero if there are unresolved deferred foreign
2476324947
** key constraints. Set *pCurrent to zero if all foreign key constraints
24764
- ** have been satisfied. The *pHighwater is always set to zero.
24948
+ ** have been satisfied. The *pHighwtr is always set to zero.
2476524949
*/
2476624950
case SQLITE_DBSTATUS_DEFERRED_FKS: {
24767
- *pHighwater = 0; /* IMP: R-11967-56545 */
24951
+ *pHighwtr = 0; /* IMP: R-11967-56545 */
2476824952
*pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
2476924953
break;
2477024954
}
2477124955
2477224956
default: {
@@ -24774,10 +24958,35 @@
2477424958
}
2477524959
}
2477624960
sqlite3_mutex_leave(db->mutex);
2477724961
return rc;
2477824962
}
24963
+
24964
+/*
24965
+** 32-bit variant of sqlite3_db_status64()
24966
+*/
24967
+SQLITE_API int sqlite3_db_status(
24968
+ sqlite3 *db, /* The database connection whose status is desired */
24969
+ int op, /* Status verb */
24970
+ int *pCurrent, /* Write current value here */
24971
+ int *pHighwtr, /* Write high-water mark here */
24972
+ int resetFlag /* Reset high-water mark if true */
24973
+){
24974
+ sqlite3_int64 C = 0, H = 0;
24975
+ int rc;
24976
+#ifdef SQLITE_ENABLE_API_ARMOR
24977
+ if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwtr==0 ){
24978
+ return SQLITE_MISUSE_BKPT;
24979
+ }
24980
+#endif
24981
+ rc = sqlite3_db_status64(db, op, &C, &H, resetFlag);
24982
+ if( rc==0 ){
24983
+ *pCurrent = C & 0x7fffffff;
24984
+ *pHighwtr = H & 0x7fffffff;
24985
+ }
24986
+ return rc;
24987
+}
2477924988
2478024989
/************** End of status.c **********************************************/
2478124990
/************** Begin file date.c ********************************************/
2478224991
/*
2478324992
** 2003 October 31
@@ -24967,10 +25176,14 @@
2496725176
if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
2496825177
return 1;
2496925178
}
2497025179
zDate += 5;
2497125180
p->tz = sgn*(nMn + nHr*60);
25181
+ if( p->tz==0 ){ /* Forum post 2025-09-17T10:12:14z */
25182
+ p->isLocal = 0;
25183
+ p->isUtc = 1;
25184
+ }
2497225185
zulu_time:
2497325186
while( sqlite3Isspace(*zDate) ){ zDate++; }
2497425187
return *zDate!=0;
2497525188
}
2497625189
@@ -26162,12 +26375,12 @@
2616226375
** %j day of year 001-366
2616326376
** %J ** julian day number
2616426377
** %l hour 1-12 (leading zero converted to space)
2616526378
** %m month 01-12
2616626379
** %M minute 00-59
26167
-** %p "am" or "pm"
26168
-** %P "AM" or "PM"
26380
+** %p "AM" or "PM"
26381
+** %P "am" or "pm"
2616926382
** %R time as HH:MM
2617026383
** %s seconds since 1970-01-01
2617126384
** %S seconds 00-59
2617226385
** %T time as HH:MM:SS
2617326386
** %u day of week 1-7 Monday==1, Sunday==7
@@ -31770,56 +31983,74 @@
3177031983
etByte base; /* The base for radix conversion */
3177131984
etByte flags; /* One or more of FLAG_ constants below */
3177231985
etByte type; /* Conversion paradigm */
3177331986
etByte charset; /* Offset into aDigits[] of the digits string */
3177431987
etByte prefix; /* Offset into aPrefix[] of the prefix string */
31988
+ char iNxt; /* Next with same hash, or 0 for end of chain */
3177531989
} et_info;
3177631990
3177731991
/*
3177831992
** Allowed values for et_info.flags
3177931993
*/
3178031994
#define FLAG_SIGNED 1 /* True if the value to convert is signed */
3178131995
#define FLAG_STRING 4 /* Allow infinite precision */
3178231996
31997
+/*
31998
+** The table is searched by hash. In the case of %C where C is the character
31999
+** and that character has ASCII value j, then the hash is j%23.
32000
+**
32001
+** The order of the entries in fmtinfo[] and the hash chain was entered
32002
+** manually, but based on the output of the following TCL script:
32003
+*/
32004
+#if 0 /***** Beginning of script ******/
32005
+foreach c {d s g z q Q w c o u x X f e E G i n % p T S r} {
32006
+ scan $c %c x
32007
+ set n($c) $x
32008
+}
32009
+set mx [llength [array names n]]
32010
+puts "count: $mx"
3178332011
31784
-/*
31785
-** The following table is searched linearly, so it is good to put the
31786
-** most frequently used conversion types first.
31787
-*/
32012
+set mx 27
32013
+puts "*********** mx=$mx ************"
32014
+for {set r 0} {$r<$mx} {incr r} {
32015
+ puts -nonewline [format %2d: $r]
32016
+ foreach c [array names n] {
32017
+ if {($n($c))%$mx==$r} {puts -nonewline " $c"}
32018
+ }
32019
+ puts ""
32020
+}
32021
+#endif /***** End of script ********/
32022
+
3178832023
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
3178932024
static const char aPrefix[] = "-x0\000X0";
31790
-static const et_info fmtinfo[] = {
31791
- { 'd', 10, 1, etDECIMAL, 0, 0 },
31792
- { 's', 0, 4, etSTRING, 0, 0 },
31793
- { 'g', 0, 1, etGENERIC, 30, 0 },
31794
- { 'z', 0, 4, etDYNSTRING, 0, 0 },
31795
- { 'q', 0, 4, etESCAPE_q, 0, 0 },
31796
- { 'Q', 0, 4, etESCAPE_Q, 0, 0 },
31797
- { 'w', 0, 4, etESCAPE_w, 0, 0 },
31798
- { 'c', 0, 0, etCHARX, 0, 0 },
31799
- { 'o', 8, 0, etRADIX, 0, 2 },
31800
- { 'u', 10, 0, etDECIMAL, 0, 0 },
31801
- { 'x', 16, 0, etRADIX, 16, 1 },
31802
- { 'X', 16, 0, etRADIX, 0, 4 },
31803
-#ifndef SQLITE_OMIT_FLOATING_POINT
31804
- { 'f', 0, 1, etFLOAT, 0, 0 },
31805
- { 'e', 0, 1, etEXP, 30, 0 },
31806
- { 'E', 0, 1, etEXP, 14, 0 },
31807
- { 'G', 0, 1, etGENERIC, 14, 0 },
31808
-#endif
31809
- { 'i', 10, 1, etDECIMAL, 0, 0 },
31810
- { 'n', 0, 0, etSIZE, 0, 0 },
31811
- { '%', 0, 0, etPERCENT, 0, 0 },
31812
- { 'p', 16, 0, etPOINTER, 0, 1 },
31813
-
31814
- /* All the rest are undocumented and are for internal use only */
31815
- { 'T', 0, 0, etTOKEN, 0, 0 },
31816
- { 'S', 0, 0, etSRCITEM, 0, 0 },
31817
- { 'r', 10, 1, etORDINAL, 0, 0 },
32025
+static const et_info fmtinfo[23] = {
32026
+ /* 0 */ { 's', 0, 4, etSTRING, 0, 0, 1 },
32027
+ /* 1 */ { 'E', 0, 1, etEXP, 14, 0, 0 }, /* Hash: 0 */
32028
+ /* 2 */ { 'u', 10, 0, etDECIMAL, 0, 0, 3 },
32029
+ /* 3 */ { 'G', 0, 1, etGENERIC, 14, 0, 0 }, /* Hash: 2 */
32030
+ /* 4 */ { 'w', 0, 4, etESCAPE_w, 0, 0, 0 },
32031
+ /* 5 */ { 'x', 16, 0, etRADIX, 16, 1, 0 },
32032
+ /* 6 */ { 'c', 0, 0, etCHARX, 0, 0, 0 }, /* Hash: 7 */
32033
+ /* 7 */ { 'z', 0, 4, etDYNSTRING, 0, 0, 6 },
32034
+ /* 8 */ { 'd', 10, 1, etDECIMAL, 0, 0, 0 },
32035
+ /* 9 */ { 'e', 0, 1, etEXP, 30, 0, 0 },
32036
+ /* 10 */ { 'f', 0, 1, etFLOAT, 0, 0, 0 },
32037
+ /* 11 */ { 'g', 0, 1, etGENERIC, 30, 0, 0 },
32038
+ /* 12 */ { 'Q', 0, 4, etESCAPE_Q, 0, 0, 0 },
32039
+ /* 13 */ { 'i', 10, 1, etDECIMAL, 0, 0, 0 },
32040
+ /* 14 */ { '%', 0, 0, etPERCENT, 0, 0, 16 },
32041
+ /* 15 */ { 'T', 0, 0, etTOKEN, 0, 0, 0 },
32042
+ /* 16 */ { 'S', 0, 0, etSRCITEM, 0, 0, 0 }, /* Hash: 14 */
32043
+ /* 17 */ { 'X', 16, 0, etRADIX, 0, 4, 0 }, /* Hash: 19 */
32044
+ /* 18 */ { 'n', 0, 0, etSIZE, 0, 0, 0 },
32045
+ /* 19 */ { 'o', 8, 0, etRADIX, 0, 2, 17 },
32046
+ /* 20 */ { 'p', 16, 0, etPOINTER, 0, 1, 0 },
32047
+ /* 21 */ { 'q', 0, 4, etESCAPE_q, 0, 0, 0 },
32048
+ /* 22 */ { 'r', 10, 1, etORDINAL, 0, 0, 0 }
3181832049
};
3181932050
31820
-/* Notes:
32051
+/* Additional Notes:
3182132052
**
3182232053
** %S Takes a pointer to SrcItem. Shows name or database.name
3182332054
** %!S Like %S but prefer the zName over the zAlias
3182432055
*/
3182532056
@@ -31942,11 +32173,14 @@
3194232173
if( c!='%' ){
3194332174
bufpt = (char *)fmt;
3194432175
#if HAVE_STRCHRNUL
3194532176
fmt = strchrnul(fmt, '%');
3194632177
#else
31947
- do{ fmt++; }while( *fmt && *fmt != '%' );
32178
+ fmt = strchr(fmt, '%');
32179
+ if( fmt==0 ){
32180
+ fmt = bufpt + strlen(bufpt);
32181
+ }
3194832182
#endif
3194932183
sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
3195032184
if( *fmt==0 ) break;
3195132185
}
3195232186
if( (c=(*++fmt))==0 ){
@@ -32056,19 +32290,36 @@
3205632290
}
3205732291
}
3205832292
}while( !done && (c=(*++fmt))!=0 );
3205932293
3206032294
/* Fetch the info entry for the field */
32295
+#ifdef SQLITE_EBCDIC
32296
+ /* The hash table only works for ASCII. For EBCDIC, we need to do
32297
+ ** a linear search of the table */
3206132298
infop = &fmtinfo[0];
3206232299
xtype = etINVALID;
3206332300
for(idx=0; idx<ArraySize(fmtinfo); idx++){
3206432301
if( c==fmtinfo[idx].fmttype ){
3206532302
infop = &fmtinfo[idx];
3206632303
xtype = infop->type;
3206732304
break;
3206832305
}
3206932306
}
32307
+#else
32308
+ /* Fast hash-table lookup */
32309
+ assert( ArraySize(fmtinfo)==23 );
32310
+ idx = ((unsigned)c) % 23;
32311
+ if( fmtinfo[idx].fmttype==c
32312
+ || fmtinfo[idx = fmtinfo[idx].iNxt].fmttype==c
32313
+ ){
32314
+ infop = &fmtinfo[idx];
32315
+ xtype = infop->type;
32316
+ }else{
32317
+ infop = &fmtinfo[0];
32318
+ xtype = etINVALID;
32319
+ }
32320
+#endif
3207032321
3207132322
/*
3207232323
** At this point, variables are initialized as follows:
3207332324
**
3207432325
** flag_alternateform TRUE if a '#' is present.
@@ -32252,11 +32503,25 @@
3225232503
length = sqlite3Strlen30(bufpt);
3225332504
break;
3225432505
}
3225532506
}
3225632507
if( s.sign=='-' ){
32257
- prefix = '-';
32508
+ if( flag_alternateform
32509
+ && !flag_prefix
32510
+ && xtype==etFLOAT
32511
+ && s.iDP<=iRound
32512
+ ){
32513
+ /* Suppress the minus sign if all of the following are true:
32514
+ ** * The value displayed is zero
32515
+ ** * The '#' flag is used
32516
+ ** * The '+' flag is not used, and
32517
+ ** * The format is %f
32518
+ */
32519
+ prefix = 0;
32520
+ }else{
32521
+ prefix = '-';
32522
+ }
3225832523
}else{
3225932524
prefix = flag_prefix;
3226032525
}
3226132526
3226232527
exp = s.iDP-1;
@@ -33463,13 +33728,17 @@
3346333728
sqlite3StrAccumFinish(&x);
3346433729
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
3346533730
n = 0;
3346633731
if( pItem->fg.isSubquery ) n++;
3346733732
if( pItem->fg.isTabFunc ) n++;
33468
- if( pItem->fg.isUsing ) n++;
33733
+ if( pItem->fg.isUsing || pItem->u3.pOn!=0 ) n++;
3346933734
if( pItem->fg.isUsing ){
3347033735
sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
33736
+ }else if( pItem->u3.pOn!=0 ){
33737
+ sqlite3TreeViewItem(pView, "ON", (--n)>0);
33738
+ sqlite3TreeViewExpr(pView, pItem->u3.pOn, 0);
33739
+ sqlite3TreeViewPop(&pView);
3347133740
}
3347233741
if( pItem->fg.isSubquery ){
3347333742
assert( n==1 );
3347433743
if( pItem->pSTab ){
3347533744
Table *pTab = pItem->pSTab;
@@ -38108,11 +38377,11 @@
3810838377
static int kvstorageDelete(const char*, const char *zKey);
3810938378
static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
3811038379
#define KVSTORAGE_KEY_SZ 32
3811138380
3811238381
/* Expand the key name with an appropriate prefix and put the result
38113
-** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
38382
+** in zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
3811438383
** KVSTORAGE_KEY_SZ bytes.
3811538384
*/
3811638385
static void kvstorageMakeKey(
3811738386
const char *zClass,
3811838387
const char *zKeyIn,
@@ -38167,14 +38436,16 @@
3816738436
** enough to hold it all. The value put into zBuf must always be zero
3816838437
** terminated, even if it gets truncated because nBuf is not large enough.
3816938438
**
3817038439
** Return the total number of bytes in the data, without truncation, and
3817138440
** not counting the final zero terminator. Return -1 if the key does
38172
-** not exist.
38441
+** not exist or its key cannot be read.
3817338442
**
38174
-** If nBuf<=0 then this routine simply returns the size of the data without
38175
-** actually reading it.
38443
+** If nBuf<=0 then this routine simply returns the size of the data
38444
+** without actually reading it. Similarly, if nBuf==1 then it
38445
+** zero-terminates zBuf at zBuf[0] and returns the size of the data
38446
+** without reading it.
3817638447
*/
3817738448
static int kvstorageRead(
3817838449
const char *zClass,
3817938450
const char *zKey,
3818038451
char *zBuf,
@@ -38219,15 +38490,13 @@
3821938490
/*
3822038491
** An internal level of indirection which enables us to replace the
3822138492
** kvvfs i/o methods with JavaScript implementations in WASM builds.
3822238493
** Maintenance reminder: if this struct changes in any way, the JSON
3822338494
** rendering of its structure must be updated in
38224
-** sqlite3_wasm_enum_json(). There are no binary compatibility
38225
-** concerns, so it does not need an iVersion member. This file is
38226
-** necessarily always compiled together with sqlite3_wasm_enum_json(),
38227
-** and JS code dynamically creates the mapping of members based on
38228
-** that JSON description.
38495
+** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary
38496
+** compatibility concerns, so it does not need an iVersion
38497
+** member.
3822938498
*/
3823038499
typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
3823138500
struct sqlite3_kvvfs_methods {
3823238501
int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
3823338502
int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
@@ -38240,12 +38509,12 @@
3824038509
** for JavaScript-side implementations in WASM builds. In such builds
3824138510
** it cannot be const, but in native builds it should be so that
3824238511
** the compiler can hopefully optimize this level of indirection out.
3824338512
** That said, kvvfs is intended primarily for use in WASM builds.
3824438513
**
38245
-** Note that this is not explicitly flagged as static because the
38246
-** amalgamation build will tag it with SQLITE_PRIVATE.
38514
+** This is not explicitly flagged as static because the amalgamation
38515
+** build will tag it with SQLITE_PRIVATE.
3824738516
*/
3824838517
#ifndef SQLITE_WASM
3824938518
const
3825038519
#endif
3825138520
SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
@@ -39414,14 +39683,15 @@
3941439683
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
3941539684
aSyscall[13].pCurrent)
3941639685
3941739686
#if defined(HAVE_FCHMOD)
3941839687
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
39688
+#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
3941939689
#else
3942039690
{ "fchmod", (sqlite3_syscall_ptr)0, 0 },
39691
+#define osFchmod(FID,MODE) 0
3942139692
#endif
39422
-#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
3942339693
3942439694
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
3942539695
{ "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
3942639696
#else
3942739697
{ "fallocate", (sqlite3_syscall_ptr)0, 0 },
@@ -39675,13 +39945,12 @@
3967539945
return fd;
3967639946
}
3967739947
3967839948
/*
3967939949
** Helper functions to obtain and relinquish the global mutex. The
39680
-** global mutex is used to protect the unixInodeInfo and
39681
-** vxworksFileId objects used by this file, all of which may be
39682
-** shared by multiple threads.
39950
+** global mutex is used to protect the unixInodeInfo objects used by
39951
+** this file, all of which may be shared by multiple threads.
3968339952
**
3968439953
** Function unixMutexHeld() is used to assert() that the global mutex
3968539954
** is held when required. This function is only used as part of assert()
3968639955
** statements. e.g.
3968739956
**
@@ -39879,10 +40148,11 @@
3987940148
/*
3988040149
** All unique filenames are held on a linked list headed by this
3988140150
** variable:
3988240151
*/
3988340152
static struct vxworksFileId *vxworksFileList = 0;
40153
+static sqlite3_mutex *vxworksMutex = 0;
3988440154
3988540155
/*
3988640156
** Simplify a filename into its canonical form
3988740157
** by making the following changes:
3988840158
**
@@ -39944,47 +40214,47 @@
3994440214
3994540215
/* Search for an existing entry that matching the canonical name.
3994640216
** If found, increment the reference count and return a pointer to
3994740217
** the existing file ID.
3994840218
*/
39949
- unixEnterMutex();
40219
+ sqlite3_mutex_enter(vxworksMutex);
3995040220
for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){
3995140221
if( pCandidate->nName==n
3995240222
&& memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0
3995340223
){
3995440224
sqlite3_free(pNew);
3995540225
pCandidate->nRef++;
39956
- unixLeaveMutex();
40226
+ sqlite3_mutex_leave(vxworksMutex);
3995740227
return pCandidate;
3995840228
}
3995940229
}
3996040230
3996140231
/* No match was found. We will make a new file ID */
3996240232
pNew->nRef = 1;
3996340233
pNew->nName = n;
3996440234
pNew->pNext = vxworksFileList;
3996540235
vxworksFileList = pNew;
39966
- unixLeaveMutex();
40236
+ sqlite3_mutex_leave(vxworksMutex);
3996740237
return pNew;
3996840238
}
3996940239
3997040240
/*
3997140241
** Decrement the reference count on a vxworksFileId object. Free
3997240242
** the object when the reference count reaches zero.
3997340243
*/
3997440244
static void vxworksReleaseFileId(struct vxworksFileId *pId){
39975
- unixEnterMutex();
40245
+ sqlite3_mutex_enter(vxworksMutex);
3997640246
assert( pId->nRef>0 );
3997740247
pId->nRef--;
3997840248
if( pId->nRef==0 ){
3997940249
struct vxworksFileId **pp;
3998040250
for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){}
3998140251
assert( *pp==pId );
3998240252
*pp = pId->pNext;
3998340253
sqlite3_free(pId);
3998440254
}
39985
- unixLeaveMutex();
40255
+ sqlite3_mutex_leave(vxworksMutex);
3998640256
}
3998740257
#endif /* OS_VXWORKS */
3998840258
/*************** End of Unique File ID Utility Used By VxWorks ****************
3998940259
******************************************************************************/
3999040260
@@ -40368,10 +40638,14 @@
4036840638
do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
4036940639
if( rc!=1 ){
4037040640
storeLastErrno(pFile, errno);
4037140641
return SQLITE_IOERR;
4037240642
}
40643
+ if( fsync(fd) ){
40644
+ storeLastErrno(pFile, errno);
40645
+ return SQLITE_IOERR_FSYNC;
40646
+ }
4037340647
rc = osFstat(fd, &statbuf);
4037440648
if( rc!=0 ){
4037540649
storeLastErrno(pFile, errno);
4037640650
return SQLITE_IOERR;
4037740651
}
@@ -40537,22 +40811,46 @@
4053740811
static int osSetPosixAdvisoryLock(
4053840812
int h, /* The file descriptor on which to take the lock */
4053940813
struct flock *pLock, /* The description of the lock */
4054040814
unixFile *pFile /* Structure holding timeout value */
4054140815
){
40542
- int tm = pFile->iBusyTimeout;
40543
- int rc = osFcntl(h,F_SETLK,pLock);
40544
- while( rc<0 && tm>0 ){
40545
- /* On systems that support some kind of blocking file lock with a timeout,
40546
- ** make appropriate changes here to invoke that blocking file lock. On
40547
- ** generic posix, however, there is no such API. So we simply try the
40548
- ** lock once every millisecond until either the timeout expires, or until
40549
- ** the lock is obtained. */
40550
- unixSleep(0,1000);
40816
+ int rc = 0;
40817
+
40818
+ if( pFile->iBusyTimeout==0 ){
40819
+ /* unixFile->iBusyTimeout is set to 0. In this case, attempt a
40820
+ ** non-blocking lock. */
40821
+ rc = osFcntl(h,F_SETLK,pLock);
40822
+ }else{
40823
+ /* unixFile->iBusyTimeout is set to greater than zero. In this case,
40824
+ ** attempt a blocking-lock with a unixFile->iBusyTimeout ms timeout.
40825
+ **
40826
+ ** On systems that support some kind of blocking file lock operation,
40827
+ ** this block should be replaced by code to attempt a blocking lock
40828
+ ** with a timeout of unixFile->iBusyTimeout ms. The code below is
40829
+ ** placeholder code. If SQLITE_TEST is defined, the placeholder code
40830
+ ** retries the lock once every 1ms until it succeeds or the timeout
40831
+ ** is reached. Or, if SQLITE_TEST is not defined, the placeholder
40832
+ ** code attempts a non-blocking lock and sets unixFile->iBusyTimeout
40833
+ ** to 0. This causes the caller to return SQLITE_BUSY, instead of
40834
+ ** SQLITE_BUSY_TIMEOUT to SQLite - as required by a VFS that does not
40835
+ ** support blocking locks.
40836
+ */
40837
+#ifdef SQLITE_TEST
40838
+ int tm = pFile->iBusyTimeout;
40839
+ while( tm>0 ){
40840
+ rc = osFcntl(h,F_SETLK,pLock);
40841
+ if( rc==0 ) break;
40842
+ unixSleep(0,1000);
40843
+ tm--;
40844
+ }
40845
+#else
4055140846
rc = osFcntl(h,F_SETLK,pLock);
40552
- tm--;
40847
+ pFile->iBusyTimeout = 0;
40848
+#endif
40849
+ /* End of code to replace with real blocking-locks code. */
4055340850
}
40851
+
4055440852
return rc;
4055540853
}
4055640854
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
4055740855
4055840856
@@ -44869,14 +45167,21 @@
4486945167
#endif
4487045168
4487145169
storeLastErrno(pNew, 0);
4487245170
#if OS_VXWORKS
4487345171
if( rc!=SQLITE_OK ){
44874
- if( h>=0 ) robust_close(pNew, h, __LINE__);
44875
- h = -1;
44876
- osUnlink(zFilename);
44877
- pNew->ctrlFlags |= UNIXFILE_DELETE;
45172
+ if( h>=0 ){
45173
+ robust_close(pNew, h, __LINE__);
45174
+ h = -1;
45175
+ }
45176
+ if( pNew->ctrlFlags & UNIXFILE_DELETE ){
45177
+ osUnlink(zFilename);
45178
+ }
45179
+ if( pNew->pId ){
45180
+ vxworksReleaseFileId(pNew->pId);
45181
+ pNew->pId = 0;
45182
+ }
4487845183
}
4487945184
#endif
4488045185
if( rc!=SQLITE_OK ){
4488145186
if( h>=0 ) robust_close(pNew, h, __LINE__);
4488245187
}else{
@@ -44916,10 +45221,13 @@
4491645221
struct stat buf;
4491745222
const char *zDir = sqlite3_temp_directory;
4491845223
4491945224
while(1){
4492045225
if( zDir!=0
45226
+#if OS_VXWORKS
45227
+ && zDir[0]=='/'
45228
+#endif
4492145229
&& osStat(zDir, &buf)==0
4492245230
&& S_ISDIR(buf.st_mode)
4492345231
&& osAccess(zDir, 03)==0
4492445232
){
4492545233
return zDir;
@@ -45229,10 +45537,16 @@
4522945537
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
4523045538
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
4523145539
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
4523245540
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
4523345541
);
45542
+
45543
+#if OS_VXWORKS
45544
+ /* The file-ID mechanism used in Vxworks requires that all pathnames
45545
+ ** provided to unixOpen must be absolute pathnames. */
45546
+ if( zPath!=0 && zPath[0]!='/' ){ return SQLITE_CANTOPEN; }
45547
+#endif
4523445548
4523545549
/* Detect a pid change and reset the PRNG. There is a race condition
4523645550
** here such that two or more threads all trying to open databases at
4523745551
** the same instant might all reset the PRNG. But multiple resets
4523845552
** are harmless.
@@ -45430,12 +45744,15 @@
4543045744
goto open_finished;
4543145745
}
4543245746
}
4543345747
#endif
4543445748
45435
- assert( zPath==0 || zPath[0]=='/'
45436
- || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL
45749
+ assert( zPath==0
45750
+ || zPath[0]=='/'
45751
+ || eType==SQLITE_OPEN_SUPER_JOURNAL
45752
+ || eType==SQLITE_OPEN_MAIN_JOURNAL
45753
+ || eType==SQLITE_OPEN_TEMP_JOURNAL
4543745754
);
4543845755
rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
4543945756
4544045757
open_finished:
4544145758
if( rc!=SQLITE_OK ){
@@ -47160,10 +47477,13 @@
4716047477
}
4716147478
#ifdef SQLITE_OS_KV_OPTIONAL
4716247479
sqlite3KvvfsInit();
4716347480
#endif
4716447481
unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
47482
+#if OS_VXWORKS
47483
+ vxworksMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS2);
47484
+#endif
4716547485
4716647486
#ifndef SQLITE_OMIT_WAL
4716747487
/* Validate lock assumptions */
4716847488
assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */
4716947489
assert( UNIX_SHM_BASE==120 ); /* Start of locking area */
@@ -47194,10 +47514,13 @@
4719447514
** to release dynamically allocated objects. But not on unix.
4719547515
** This routine is a no-op for unix.
4719647516
*/
4719747517
SQLITE_API int sqlite3_os_end(void){
4719847518
unixBigLock = 0;
47519
+#if OS_VXWORKS
47520
+ vxworksMutex = 0;
47521
+#endif
4719947522
return SQLITE_OK;
4720047523
}
4720147524
4720247525
#endif /* SQLITE_OS_UNIX */
4720347526
@@ -51192,204 +51515,10 @@
5119251515
** on allocation size granularity boundaries.
5119351516
** During sqlite3_os_init() we do a GetSystemInfo()
5119451517
** to get the granularity size.
5119551518
*/
5119651519
static SYSTEM_INFO winSysInfo;
51197
-
51198
-#ifndef SQLITE_OMIT_WAL
51199
-
51200
-/*
51201
-** Helper functions to obtain and relinquish the global mutex. The
51202
-** global mutex is used to protect the winLockInfo objects used by
51203
-** this file, all of which may be shared by multiple threads.
51204
-**
51205
-** Function winShmMutexHeld() is used to assert() that the global mutex
51206
-** is held when required. This function is only used as part of assert()
51207
-** statements. e.g.
51208
-**
51209
-** winShmEnterMutex()
51210
-** assert( winShmMutexHeld() );
51211
-** winShmLeaveMutex()
51212
-*/
51213
-static sqlite3_mutex *winBigLock = 0;
51214
-static void winShmEnterMutex(void){
51215
- sqlite3_mutex_enter(winBigLock);
51216
-}
51217
-static void winShmLeaveMutex(void){
51218
- sqlite3_mutex_leave(winBigLock);
51219
-}
51220
-#ifndef NDEBUG
51221
-static int winShmMutexHeld(void) {
51222
- return sqlite3_mutex_held(winBigLock);
51223
-}
51224
-#endif
51225
-
51226
-/*
51227
-** Object used to represent a single file opened and mmapped to provide
51228
-** shared memory. When multiple threads all reference the same
51229
-** log-summary, each thread has its own winFile object, but they all
51230
-** point to a single instance of this object. In other words, each
51231
-** log-summary is opened only once per process.
51232
-**
51233
-** winShmMutexHeld() must be true when creating or destroying
51234
-** this object or while reading or writing the following fields:
51235
-**
51236
-** nRef
51237
-** pNext
51238
-**
51239
-** The following fields are read-only after the object is created:
51240
-**
51241
-** zFilename
51242
-**
51243
-** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
51244
-** winShmMutexHeld() is true when reading or writing any other field
51245
-** in this structure.
51246
-**
51247
-** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate
51248
-** the *-shm file if the DMS-locking protocol demands it, and (c) map
51249
-** regions of the *-shm file into memory using MapViewOfFile() or
51250
-** similar. Other locks are taken by individual clients using the
51251
-** winShm.hShm handles.
51252
-*/
51253
-struct winShmNode {
51254
- sqlite3_mutex *mutex; /* Mutex to access this object */
51255
- char *zFilename; /* Name of the file */
51256
- HANDLE hSharedShm; /* File handle open on zFilename */
51257
-
51258
- int isUnlocked; /* DMS lock has not yet been obtained */
51259
- int isReadonly; /* True if read-only */
51260
- int szRegion; /* Size of shared-memory regions */
51261
- int nRegion; /* Size of array apRegion */
51262
-
51263
- struct ShmRegion {
51264
- HANDLE hMap; /* File handle from CreateFileMapping */
51265
- void *pMap;
51266
- } *aRegion;
51267
- DWORD lastErrno; /* The Windows errno from the last I/O error */
51268
-
51269
- int nRef; /* Number of winShm objects pointing to this */
51270
- winShmNode *pNext; /* Next in list of all winShmNode objects */
51271
-#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51272
- u8 nextShmId; /* Next available winShm.id value */
51273
-#endif
51274
-};
51275
-
51276
-/*
51277
-** A global array of all winShmNode objects.
51278
-**
51279
-** The winShmMutexHeld() must be true while reading or writing this list.
51280
-*/
51281
-static winShmNode *winShmNodeList = 0;
51282
-
51283
-/*
51284
-** Structure used internally by this VFS to record the state of an
51285
-** open shared memory connection. There is one such structure for each
51286
-** winFile open on a wal mode database.
51287
-*/
51288
-struct winShm {
51289
- winShmNode *pShmNode; /* The underlying winShmNode object */
51290
- u16 sharedMask; /* Mask of shared locks held */
51291
- u16 exclMask; /* Mask of exclusive locks held */
51292
- HANDLE hShm; /* File-handle on *-shm file. For locking. */
51293
- int bReadonly; /* True if hShm is opened read-only */
51294
-#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51295
- u8 id; /* Id of this connection with its winShmNode */
51296
-#endif
51297
-};
51298
-
51299
-/*
51300
-** Constants used for locking
51301
-*/
51302
-#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
51303
-#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
51304
-
51305
-/* Forward references to VFS methods */
51306
-static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
51307
-static int winDelete(sqlite3_vfs *,const char*,int);
51308
-
51309
-/*
51310
-** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
51311
-**
51312
-** This is not a VFS shared-memory method; it is a utility function called
51313
-** by VFS shared-memory methods.
51314
-*/
51315
-static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
51316
- winShmNode **pp;
51317
- winShmNode *p;
51318
- assert( winShmMutexHeld() );
51319
- OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
51320
- osGetCurrentProcessId(), deleteFlag));
51321
- pp = &winShmNodeList;
51322
- while( (p = *pp)!=0 ){
51323
- if( p->nRef==0 ){
51324
- int i;
51325
- if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
51326
- for(i=0; i<p->nRegion; i++){
51327
- BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
51328
- OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
51329
- osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51330
- UNUSED_VARIABLE_VALUE(bRc);
51331
- bRc = osCloseHandle(p->aRegion[i].hMap);
51332
- OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
51333
- osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51334
- UNUSED_VARIABLE_VALUE(bRc);
51335
- }
51336
- winHandleClose(p->hSharedShm);
51337
- if( deleteFlag ){
51338
- SimulateIOErrorBenign(1);
51339
- sqlite3BeginBenignMalloc();
51340
- winDelete(pVfs, p->zFilename, 0);
51341
- sqlite3EndBenignMalloc();
51342
- SimulateIOErrorBenign(0);
51343
- }
51344
- *pp = p->pNext;
51345
- sqlite3_free(p->aRegion);
51346
- sqlite3_free(p);
51347
- }else{
51348
- pp = &p->pNext;
51349
- }
51350
- }
51351
-}
51352
-
51353
-/*
51354
-** The DMS lock has not yet been taken on the shm file associated with
51355
-** pShmNode. Take the lock. Truncate the *-shm file if required.
51356
-** Return SQLITE_OK if successful, or an SQLite error code otherwise.
51357
-*/
51358
-static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
51359
- HANDLE h = pShmNode->hSharedShm;
51360
- int rc = SQLITE_OK;
51361
-
51362
- assert( sqlite3_mutex_held(pShmNode->mutex) );
51363
- rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
51364
- if( rc==SQLITE_OK ){
51365
- /* We have an EXCLUSIVE lock on the DMS byte. This means that this
51366
- ** is the first process to open the file. Truncate it to zero bytes
51367
- ** in this case. */
51368
- if( pShmNode->isReadonly ){
51369
- rc = SQLITE_READONLY_CANTINIT;
51370
- }else{
51371
- rc = winHandleTruncate(h, 0);
51372
- }
51373
-
51374
- /* Release the EXCLUSIVE lock acquired above. */
51375
- winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
51376
- }else if( (rc & 0xFF)==SQLITE_BUSY ){
51377
- rc = SQLITE_OK;
51378
- }
51379
-
51380
- if( rc==SQLITE_OK ){
51381
- /* Take a SHARED lock on the DMS byte. */
51382
- rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
51383
- if( rc==SQLITE_OK ){
51384
- pShmNode->isUnlocked = 0;
51385
- }
51386
- }
51387
-
51388
- return rc;
51389
-}
51390
-
5139151520
5139251521
/*
5139351522
** Convert a UTF-8 filename into whatever form the underlying
5139451523
** operating system wants filenames in. Space to hold the result
5139551524
** is obtained from malloc and must be freed by the calling
@@ -51483,10 +51612,212 @@
5148351612
}
5148451613
#endif
5148551614
/* caller will handle out of memory */
5148651615
return zConverted;
5148751616
}
51617
+
51618
+#ifndef SQLITE_OMIT_WAL
51619
+
51620
+/*
51621
+** Helper functions to obtain and relinquish the global mutex. The
51622
+** global mutex is used to protect the winLockInfo objects used by
51623
+** this file, all of which may be shared by multiple threads.
51624
+**
51625
+** Function winShmMutexHeld() is used to assert() that the global mutex
51626
+** is held when required. This function is only used as part of assert()
51627
+** statements. e.g.
51628
+**
51629
+** winShmEnterMutex()
51630
+** assert( winShmMutexHeld() );
51631
+** winShmLeaveMutex()
51632
+*/
51633
+static sqlite3_mutex *winBigLock = 0;
51634
+static void winShmEnterMutex(void){
51635
+ sqlite3_mutex_enter(winBigLock);
51636
+}
51637
+static void winShmLeaveMutex(void){
51638
+ sqlite3_mutex_leave(winBigLock);
51639
+}
51640
+#ifndef NDEBUG
51641
+static int winShmMutexHeld(void) {
51642
+ return sqlite3_mutex_held(winBigLock);
51643
+}
51644
+#endif
51645
+
51646
+/*
51647
+** Object used to represent a single file opened and mmapped to provide
51648
+** shared memory. When multiple threads all reference the same
51649
+** log-summary, each thread has its own winFile object, but they all
51650
+** point to a single instance of this object. In other words, each
51651
+** log-summary is opened only once per process.
51652
+**
51653
+** winShmMutexHeld() must be true when creating or destroying
51654
+** this object, or while editing the global linked list that starts
51655
+** at winShmNodeList.
51656
+**
51657
+** When reading or writing the linked list starting at winShmNode.pWinShmList,
51658
+** pShmNode->mutex must be held.
51659
+**
51660
+** The following fields are constant after the object is created:
51661
+**
51662
+** zFilename
51663
+** hSharedShm
51664
+** mutex
51665
+** bUseSharedLockHandle
51666
+**
51667
+** Either winShmNode.mutex must be held or winShmNode.pWinShmList==0 and
51668
+** winShmMutexHeld() is true when reading or writing any other field
51669
+** in this structure.
51670
+**
51671
+** File-handle hSharedShm is always used to (a) take the DMS lock, (b)
51672
+** truncate the *-shm file if the DMS-locking protocol demands it, and
51673
+** (c) map regions of the *-shm file into memory using MapViewOfFile()
51674
+** or similar. If bUseSharedLockHandle is true, then other locks are also
51675
+** taken on hSharedShm. Or, if bUseSharedLockHandle is false, then other
51676
+** locks are taken using each connection's winShm.hShm handles.
51677
+*/
51678
+struct winShmNode {
51679
+ sqlite3_mutex *mutex; /* Mutex to access this object */
51680
+ char *zFilename; /* Name of the file */
51681
+ HANDLE hSharedShm; /* File handle open on zFilename */
51682
+ int bUseSharedLockHandle; /* True to use hSharedShm for everything */
51683
+
51684
+ int isUnlocked; /* DMS lock has not yet been obtained */
51685
+ int isReadonly; /* True if read-only */
51686
+ int szRegion; /* Size of shared-memory regions */
51687
+ int nRegion; /* Size of array apRegion */
51688
+
51689
+ struct ShmRegion {
51690
+ HANDLE hMap; /* File handle from CreateFileMapping */
51691
+ void *pMap;
51692
+ } *aRegion;
51693
+ DWORD lastErrno; /* The Windows errno from the last I/O error */
51694
+
51695
+ winShm *pWinShmList; /* List of winShm objects with ptrs to this */
51696
+
51697
+ winShmNode *pNext; /* Next in list of all winShmNode objects */
51698
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51699
+ u8 nextShmId; /* Next available winShm.id value */
51700
+#endif
51701
+};
51702
+
51703
+/*
51704
+** A global array of all winShmNode objects.
51705
+**
51706
+** The winShmMutexHeld() must be true while reading or writing this list.
51707
+*/
51708
+static winShmNode *winShmNodeList = 0;
51709
+
51710
+/*
51711
+** Structure used internally by this VFS to record the state of an
51712
+** open shared memory connection. There is one such structure for each
51713
+** winFile open on a wal mode database.
51714
+*/
51715
+struct winShm {
51716
+ winShmNode *pShmNode; /* The underlying winShmNode object */
51717
+ u16 sharedMask; /* Mask of shared locks held */
51718
+ u16 exclMask; /* Mask of exclusive locks held */
51719
+ HANDLE hShm; /* File-handle on *-shm file. For locking. */
51720
+ int bReadonly; /* True if hShm is opened read-only */
51721
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51722
+ u8 id; /* Id of this connection with its winShmNode */
51723
+#endif
51724
+ winShm *pWinShmNext; /* Next winShm object on same winShmNode */
51725
+};
51726
+
51727
+/*
51728
+** Constants used for locking
51729
+*/
51730
+#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
51731
+#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
51732
+
51733
+/* Forward references to VFS methods */
51734
+static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
51735
+static int winDelete(sqlite3_vfs *,const char*,int);
51736
+
51737
+/*
51738
+** Purge the winShmNodeList list of all entries with winShmNode.pWinShmList==0.
51739
+**
51740
+** This is not a VFS shared-memory method; it is a utility function called
51741
+** by VFS shared-memory methods.
51742
+*/
51743
+static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
51744
+ winShmNode **pp;
51745
+ winShmNode *p;
51746
+ assert( winShmMutexHeld() );
51747
+ OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
51748
+ osGetCurrentProcessId(), deleteFlag));
51749
+ pp = &winShmNodeList;
51750
+ while( (p = *pp)!=0 ){
51751
+ if( p->pWinShmList==0 ){
51752
+ int i;
51753
+ if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
51754
+ for(i=0; i<p->nRegion; i++){
51755
+ BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
51756
+ OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
51757
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51758
+ UNUSED_VARIABLE_VALUE(bRc);
51759
+ bRc = osCloseHandle(p->aRegion[i].hMap);
51760
+ OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
51761
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51762
+ UNUSED_VARIABLE_VALUE(bRc);
51763
+ }
51764
+ winHandleClose(p->hSharedShm);
51765
+ if( deleteFlag ){
51766
+ SimulateIOErrorBenign(1);
51767
+ sqlite3BeginBenignMalloc();
51768
+ winDelete(pVfs, p->zFilename, 0);
51769
+ sqlite3EndBenignMalloc();
51770
+ SimulateIOErrorBenign(0);
51771
+ }
51772
+ *pp = p->pNext;
51773
+ sqlite3_free(p->aRegion);
51774
+ sqlite3_free(p);
51775
+ }else{
51776
+ pp = &p->pNext;
51777
+ }
51778
+ }
51779
+}
51780
+
51781
+/*
51782
+** The DMS lock has not yet been taken on the shm file associated with
51783
+** pShmNode. Take the lock. Truncate the *-shm file if required.
51784
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
51785
+*/
51786
+static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
51787
+ HANDLE h = pShmNode->hSharedShm;
51788
+ int rc = SQLITE_OK;
51789
+
51790
+ assert( sqlite3_mutex_held(pShmNode->mutex) );
51791
+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
51792
+ if( rc==SQLITE_OK ){
51793
+ /* We have an EXCLUSIVE lock on the DMS byte. This means that this
51794
+ ** is the first process to open the file. Truncate it to zero bytes
51795
+ ** in this case. */
51796
+ if( pShmNode->isReadonly ){
51797
+ rc = SQLITE_READONLY_CANTINIT;
51798
+ }else{
51799
+ rc = winHandleTruncate(h, 0);
51800
+ }
51801
+
51802
+ /* Release the EXCLUSIVE lock acquired above. */
51803
+ winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
51804
+ }else if( (rc & 0xFF)==SQLITE_BUSY ){
51805
+ rc = SQLITE_OK;
51806
+ }
51807
+
51808
+ if( rc==SQLITE_OK ){
51809
+ /* Take a SHARED lock on the DMS byte. */
51810
+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
51811
+ if( rc==SQLITE_OK ){
51812
+ pShmNode->isUnlocked = 0;
51813
+ }
51814
+ }
51815
+
51816
+ return rc;
51817
+}
51818
+
5148851819
5148951820
/*
5149051821
** This function is used to open a handle on a *-shm file.
5149151822
**
5149251823
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
@@ -51579,10 +51910,64 @@
5157951910
*pbReadonly = bReadonly;
5158051911
*ph = h;
5158151912
return rc;
5158251913
}
5158351914
51915
+/*
51916
+** Close pDbFd's connection to shared-memory. Delete the underlying
51917
+** *-shm file if deleteFlag is true.
51918
+*/
51919
+static int winCloseSharedMemory(winFile *pDbFd, int deleteFlag){
51920
+ winShm *p; /* The connection to be closed */
51921
+ winShm **pp; /* Iterator for pShmNode->pWinShmList */
51922
+ winShmNode *pShmNode; /* The underlying shared-memory file */
51923
+
51924
+ p = pDbFd->pShm;
51925
+ if( p==0 ) return SQLITE_OK;
51926
+ if( p->hShm!=INVALID_HANDLE_VALUE ){
51927
+ osCloseHandle(p->hShm);
51928
+ }
51929
+
51930
+ winShmEnterMutex();
51931
+ pShmNode = p->pShmNode;
51932
+
51933
+ /* Remove this connection from the winShmNode.pWinShmList list */
51934
+ sqlite3_mutex_enter(pShmNode->mutex);
51935
+ for(pp=&pShmNode->pWinShmList; *pp!=p; pp=&(*pp)->pWinShmNext){}
51936
+ *pp = p->pWinShmNext;
51937
+ sqlite3_mutex_leave(pShmNode->mutex);
51938
+
51939
+ winShmPurge(pDbFd->pVfs, deleteFlag);
51940
+ winShmLeaveMutex();
51941
+
51942
+ /* Free the connection p */
51943
+ sqlite3_free(p);
51944
+ pDbFd->pShm = 0;
51945
+ return SQLITE_OK;
51946
+}
51947
+
51948
+/*
51949
+** testfixture builds may set this global variable to true via a
51950
+** Tcl interface. This forces the VFS to use the locking normally
51951
+** only used for UNC paths for all files.
51952
+*/
51953
+#ifdef SQLITE_TEST
51954
+SQLITE_API int sqlite3_win_test_unc_locking = 0;
51955
+#else
51956
+# define sqlite3_win_test_unc_locking 0
51957
+#endif
51958
+
51959
+/*
51960
+** Return true if the string passed as the only argument is likely
51961
+** to be a UNC path. In other words, if it starts with "\\".
51962
+*/
51963
+static int winIsUNCPath(const char *zFile){
51964
+ if( zFile[0]=='\\' && zFile[1]=='\\' ){
51965
+ return 1;
51966
+ }
51967
+ return sqlite3_win_test_unc_locking;
51968
+}
5158451969
5158551970
/*
5158651971
** Open the shared-memory area associated with database file pDbFd.
5158751972
*/
5158851973
static int winOpenSharedMemory(winFile *pDbFd){
@@ -51605,19 +51990,14 @@
5160551990
return SQLITE_IOERR_NOMEM_BKPT;
5160651991
}
5160751992
pNew->zFilename = (char*)&pNew[1];
5160851993
pNew->hSharedShm = INVALID_HANDLE_VALUE;
5160951994
pNew->isUnlocked = 1;
51995
+ pNew->bUseSharedLockHandle = winIsUNCPath(pDbFd->zPath);
5161051996
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
5161151997
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
5161251998
51613
- /* Open a file-handle on the *-shm file for this connection. This file-handle
51614
- ** is only used for locking. The mapping of the *-shm file is created using
51615
- ** the shared file handle in winShmNode.hSharedShm. */
51616
- p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
51617
- rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm);
51618
-
5161951999
/* Look to see if there is an existing winShmNode that can be used.
5162052000
** If no matching winShmNode currently exists, then create a new one. */
5162152001
winShmEnterMutex();
5162252002
for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
5162352003
/* TBD need to come up with better match here. Perhaps
@@ -51634,11 +52014,11 @@
5163452014
}
5163552015
5163652016
/* Open a file-handle to use for mappings, and for the DMS lock. */
5163752017
if( rc==SQLITE_OK ){
5163852018
HANDLE h = INVALID_HANDLE_VALUE;
51639
- pShmNode->isReadonly = p->bReadonly;
52019
+ pShmNode->isReadonly = sqlite3_uri_boolean(pDbFd->zPath,"readonly_shm",0);
5164052020
rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
5164152021
pShmNode->hSharedShm = h;
5164252022
}
5164352023
5164452024
/* If successful, link the new winShmNode into the global list. If an
@@ -51656,24 +52036,39 @@
5165652036
}
5165752037
5165852038
/* If no error has occurred, link the winShm object to the winShmNode and
5165952039
** the winShm to pDbFd. */
5166052040
if( rc==SQLITE_OK ){
52041
+ sqlite3_mutex_enter(pShmNode->mutex);
5166152042
p->pShmNode = pShmNode;
51662
- pShmNode->nRef++;
52043
+ p->pWinShmNext = pShmNode->pWinShmList;
52044
+ pShmNode->pWinShmList = p;
5166352045
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
5166452046
p->id = pShmNode->nextShmId++;
5166552047
#endif
5166652048
pDbFd->pShm = p;
52049
+ sqlite3_mutex_leave(pShmNode->mutex);
5166752050
}else if( p ){
51668
- winHandleClose(p->hShm);
5166952051
sqlite3_free(p);
5167052052
}
5167152053
5167252054
assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
5167352055
winShmLeaveMutex();
5167452056
sqlite3_free(pNew);
52057
+
52058
+ /* Open a file-handle on the *-shm file for this connection. This file-handle
52059
+ ** is only used for locking. The mapping of the *-shm file is created using
52060
+ ** the shared file handle in winShmNode.hSharedShm. */
52061
+ if( rc==SQLITE_OK && pShmNode->bUseSharedLockHandle==0 ){
52062
+ p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
52063
+ rc = winHandleOpen(pShmNode->zFilename, &p->bReadonly, &p->hShm);
52064
+ if( rc!=SQLITE_OK ){
52065
+ assert( p->hShm==INVALID_HANDLE_VALUE );
52066
+ winCloseSharedMemory(pDbFd, 0);
52067
+ }
52068
+ }
52069
+
5167552070
return rc;
5167652071
}
5167752072
5167852073
/*
5167952074
** Close a connection to shared-memory. Delete the underlying
@@ -51681,37 +52076,11 @@
5168152076
*/
5168252077
static int winShmUnmap(
5168352078
sqlite3_file *fd, /* Database holding shared memory */
5168452079
int deleteFlag /* Delete after closing if true */
5168552080
){
51686
- winFile *pDbFd; /* Database holding shared-memory */
51687
- winShm *p; /* The connection to be closed */
51688
- winShmNode *pShmNode; /* The underlying shared-memory file */
51689
-
51690
- pDbFd = (winFile*)fd;
51691
- p = pDbFd->pShm;
51692
- if( p==0 ) return SQLITE_OK;
51693
- if( p->hShm!=INVALID_HANDLE_VALUE ){
51694
- osCloseHandle(p->hShm);
51695
- }
51696
-
51697
- pShmNode = p->pShmNode;
51698
- winShmEnterMutex();
51699
-
51700
- /* If pShmNode->nRef has reached 0, then close the underlying
51701
- ** shared-memory file, too. */
51702
- assert( pShmNode->nRef>0 );
51703
- pShmNode->nRef--;
51704
- if( pShmNode->nRef==0 ){
51705
- winShmPurge(pDbFd->pVfs, deleteFlag);
51706
- }
51707
- winShmLeaveMutex();
51708
-
51709
- /* Free the connection p */
51710
- sqlite3_free(p);
51711
- pDbFd->pShm = 0;
51712
- return SQLITE_OK;
52081
+ return winCloseSharedMemory((winFile*)fd, deleteFlag);
5171352082
}
5171452083
5171552084
/*
5171652085
** Change the lock state for a shared-memory segment.
5171752086
*/
@@ -51776,29 +52145,75 @@
5177652145
);
5177752146
if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
5177852147
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
5177952148
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
5178052149
){
52150
+ HANDLE h = p->hShm;
5178152151
5178252152
if( flags & SQLITE_SHM_UNLOCK ){
5178352153
/* Case (a) - unlock. */
5178452154
5178552155
assert( (p->exclMask & p->sharedMask)==0 );
5178652156
assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
5178752157
assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
5178852158
51789
- rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n);
52159
+ assert( !(flags & SQLITE_SHM_SHARED) || n==1 );
52160
+ if( pShmNode->bUseSharedLockHandle ){
52161
+ h = pShmNode->hSharedShm;
52162
+ if( flags & SQLITE_SHM_SHARED ){
52163
+ winShm *pShm;
52164
+ sqlite3_mutex_enter(pShmNode->mutex);
52165
+ for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
52166
+ if( pShm!=p && (pShm->sharedMask & mask) ){
52167
+ /* Another connection within this process is also holding this
52168
+ ** SHARED lock. So do not actually release the OS lock. */
52169
+ h = INVALID_HANDLE_VALUE;
52170
+ break;
52171
+ }
52172
+ }
52173
+ sqlite3_mutex_leave(pShmNode->mutex);
52174
+ }
52175
+ }
52176
+
52177
+ if( h!=INVALID_HANDLE_VALUE ){
52178
+ rc = winHandleUnlock(h, ofst+WIN_SHM_BASE, n);
52179
+ }
5179052180
5179152181
/* If successful, also clear the bits in sharedMask/exclMask */
5179252182
if( rc==SQLITE_OK ){
5179352183
p->exclMask = (p->exclMask & ~mask);
5179452184
p->sharedMask = (p->sharedMask & ~mask);
5179552185
}
5179652186
}else{
5179752187
int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
5179852188
DWORD nMs = winFileBusyTimeout(pDbFd);
51799
- rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs);
52189
+
52190
+ if( pShmNode->bUseSharedLockHandle ){
52191
+ winShm *pShm;
52192
+ h = pShmNode->hSharedShm;
52193
+ sqlite3_mutex_enter(pShmNode->mutex);
52194
+ for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
52195
+ if( bExcl ){
52196
+ if( (pShm->sharedMask|pShm->exclMask) & mask ){
52197
+ rc = SQLITE_BUSY;
52198
+ h = INVALID_HANDLE_VALUE;
52199
+ }
52200
+ }else{
52201
+ if( pShm->sharedMask & mask ){
52202
+ h = INVALID_HANDLE_VALUE;
52203
+ }else if( pShm->exclMask & mask ){
52204
+ rc = SQLITE_BUSY;
52205
+ h = INVALID_HANDLE_VALUE;
52206
+ }
52207
+ }
52208
+ }
52209
+ sqlite3_mutex_leave(pShmNode->mutex);
52210
+ }
52211
+
52212
+ if( h!=INVALID_HANDLE_VALUE ){
52213
+ rc = winHandleLockTimeout(h, ofst+WIN_SHM_BASE, n, bExcl, nMs);
52214
+ }
5180052215
if( rc==SQLITE_OK ){
5180152216
if( bExcl ){
5180252217
p->exclMask = (p->exclMask | mask);
5180352218
}else{
5180452219
p->sharedMask = (p->sharedMask | mask);
@@ -61825,18 +62240,31 @@
6182562240
SQLITE_PRIVATE void sqlite3PagerSetFlags(
6182662241
Pager *pPager, /* The pager to set safety level for */
6182762242
unsigned pgFlags /* Various flags */
6182862243
){
6182962244
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
61830
- if( pPager->tempFile ){
62245
+ if( pPager->tempFile || level==PAGER_SYNCHRONOUS_OFF ){
6183162246
pPager->noSync = 1;
6183262247
pPager->fullSync = 0;
6183362248
pPager->extraSync = 0;
6183462249
}else{
61835
- pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0;
62250
+ pPager->noSync = 0;
6183662251
pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
61837
- pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0;
62252
+
62253
+ /* Set Pager.extraSync if "PRAGMA synchronous=EXTRA" is requested, or
62254
+ ** if the file-system supports F2FS style atomic writes. If this flag
62255
+ ** is set, SQLite syncs the directory to disk immediately after deleting
62256
+ ** a journal file in "PRAGMA journal_mode=DELETE" mode. */
62257
+ if( level==PAGER_SYNCHRONOUS_EXTRA
62258
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
62259
+ || (sqlite3OsDeviceCharacteristics(pPager->fd) & SQLITE_IOCAP_BATCH_ATOMIC)
62260
+#endif
62261
+ ){
62262
+ pPager->extraSync = 1;
62263
+ }else{
62264
+ pPager->extraSync = 0;
62265
+ }
6183862266
}
6183962267
if( pPager->noSync ){
6184062268
pPager->syncFlags = 0;
6184162269
}else if( pgFlags & PAGER_FULLFSYNC ){
6184262270
pPager->syncFlags = SQLITE_SYNC_FULL;
@@ -65725,11 +66153,11 @@
6572566153
*/
6572666154
sqlite3_exec(db, "PRAGMA table_list",0,0,0);
6572766155
}
6572866156
if( pPager->pWal ){
6572966157
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
65730
- (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
66158
+ (eMode<=SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
6573166159
pPager->pBusyHandlerArg,
6573266160
pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
6573366161
pnLog, pnCkpt
6573466162
);
6573566163
}
@@ -70330,11 +70758,12 @@
7033070758
assert( pWal->ckptLock==0 );
7033170759
assert( pWal->writeLock==0 );
7033270760
7033370761
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
7033470762
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
70335
- assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
70763
+ assert( SQLITE_CHECKPOINT_NOOP<SQLITE_CHECKPOINT_PASSIVE );
70764
+ assert( eMode>SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
7033670765
7033770766
if( pWal->readOnly ) return SQLITE_READONLY;
7033870767
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
7033970768
7034070769
/* Enable blocking locks, if possible. */
@@ -70347,35 +70776,39 @@
7034770776
** checkpoint operation at the same time, the lock cannot be obtained and
7034870777
** SQLITE_BUSY is returned.
7034970778
** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
7035070779
** it will not be invoked in this case.
7035170780
*/
70352
- rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
70353
- testcase( rc==SQLITE_BUSY );
70354
- testcase( rc!=SQLITE_OK && xBusy2!=0 );
70355
- if( rc==SQLITE_OK ){
70356
- pWal->ckptLock = 1;
70357
-
70358
- /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
70359
- ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
70360
- ** file.
70361
- **
70362
- ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
70363
- ** immediately, and a busy-handler is configured, it is invoked and the
70364
- ** writer lock retried until either the busy-handler returns 0 or the
70365
- ** lock is successfully obtained.
70366
- */
70367
- if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
70368
- rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
70369
- if( rc==SQLITE_OK ){
70370
- pWal->writeLock = 1;
70371
- }else if( rc==SQLITE_BUSY ){
70372
- eMode2 = SQLITE_CHECKPOINT_PASSIVE;
70373
- xBusy2 = 0;
70374
- rc = SQLITE_OK;
70375
- }
70376
- }
70781
+ if( eMode!=SQLITE_CHECKPOINT_NOOP ){
70782
+ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
70783
+ testcase( rc==SQLITE_BUSY );
70784
+ testcase( rc!=SQLITE_OK && xBusy2!=0 );
70785
+ if( rc==SQLITE_OK ){
70786
+ pWal->ckptLock = 1;
70787
+
70788
+ /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART
70789
+ ** and TRUNCATE modes also obtain the exclusive "writer" lock on the
70790
+ ** database file.
70791
+ **
70792
+ ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
70793
+ ** immediately, and a busy-handler is configured, it is invoked and the
70794
+ ** writer lock retried until either the busy-handler returns 0 or the
70795
+ ** lock is successfully obtained.
70796
+ */
70797
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
70798
+ rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
70799
+ if( rc==SQLITE_OK ){
70800
+ pWal->writeLock = 1;
70801
+ }else if( rc==SQLITE_BUSY ){
70802
+ eMode2 = SQLITE_CHECKPOINT_PASSIVE;
70803
+ xBusy2 = 0;
70804
+ rc = SQLITE_OK;
70805
+ }
70806
+ }
70807
+ }
70808
+ }else{
70809
+ rc = SQLITE_OK;
7037770810
}
7037870811
7037970812
7038070813
/* Read the wal-index header. */
7038170814
SEH_TRY {
@@ -70385,21 +70818,21 @@
7038570818
** or invoke the busy handler. The only lock such a checkpoint may
7038670819
** attempt to obtain is a lock on a read-slot, and it should give up
7038770820
** immediately and do a partial checkpoint if it cannot obtain it. */
7038870821
walDisableBlocking(pWal);
7038970822
rc = walIndexReadHdr(pWal, &isChanged);
70390
- if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
70823
+ if( eMode2>SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
7039170824
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
7039270825
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
7039370826
}
7039470827
}
7039570828
7039670829
/* Copy data from the log to the database file. */
7039770830
if( rc==SQLITE_OK ){
7039870831
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
7039970832
rc = SQLITE_CORRUPT_BKPT;
70400
- }else{
70833
+ }else if( eMode2!=SQLITE_CHECKPOINT_NOOP ){
7040170834
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
7040270835
}
7040370836
7040470837
/* If no error occurred, set the output variables. */
7040570838
if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
@@ -89078,14 +89511,16 @@
8907889511
** simple case then too.
8907989512
*/
8908089513
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
8908189514
|| nTrans<=1
8908289515
){
89083
- for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89084
- Btree *pBt = db->aDb[i].pBt;
89085
- if( pBt ){
89086
- rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
89516
+ if( needXcommit ){
89517
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89518
+ Btree *pBt = db->aDb[i].pBt;
89519
+ if( sqlite3BtreeTxnState(pBt)>=SQLITE_TXN_WRITE ){
89520
+ rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
89521
+ }
8908789522
}
8908889523
}
8908989524
8909089525
/* Do the commit only if all databases successfully complete phase 1.
8909189526
** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
@@ -89092,11 +89527,13 @@
8909289527
** IO error while deleting or truncating a journal file. It is unlikely,
8909389528
** but could happen. In this case abandon processing and return the error.
8909489529
*/
8909589530
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
8909689531
Btree *pBt = db->aDb[i].pBt;
89097
- if( pBt ){
89532
+ int txn = sqlite3BtreeTxnState(pBt);
89533
+ if( txn!=SQLITE_TXN_NONE ){
89534
+ assert( needXcommit || txn==SQLITE_TXN_READ );
8909889535
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
8909989536
}
8910089537
}
8910189538
if( rc==SQLITE_OK ){
8910289539
sqlite3VtabCommit(db);
@@ -89347,32 +89784,35 @@
8934789784
return SQLITE_OK;
8934889785
}
8934989786
8935089787
8935189788
/*
89352
-** This function is called when a transaction opened by the database
89789
+** These functions are called when a transaction opened by the database
8935389790
** handle associated with the VM passed as an argument is about to be
89354
-** committed. If there are outstanding deferred foreign key constraint
89355
-** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
89791
+** committed. If there are outstanding foreign key constraint violations
89792
+** return an error code. Otherwise, SQLITE_OK.
8935689793
**
8935789794
** If there are outstanding FK violations and this function returns
89358
-** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89359
-** and write an error message to it. Then return SQLITE_ERROR.
89795
+** non-zero, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89796
+** and write an error message to it.
8936089797
*/
8936189798
#ifndef SQLITE_OMIT_FOREIGN_KEY
89362
-SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
89799
+static SQLITE_NOINLINE int vdbeFkError(Vdbe *p){
89800
+ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89801
+ p->errorAction = OE_Abort;
89802
+ sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89803
+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89804
+ return SQLITE_CONSTRAINT_FOREIGNKEY;
89805
+}
89806
+SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe *p){
89807
+ if( p->nFkConstraint==0 ) return SQLITE_OK;
89808
+ return vdbeFkError(p);
89809
+}
89810
+SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe *p){
8936389811
sqlite3 *db = p->db;
89364
- if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
89365
- || (!deferred && p->nFkConstraint>0)
89366
- ){
89367
- p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89368
- p->errorAction = OE_Abort;
89369
- sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89370
- if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89371
- return SQLITE_CONSTRAINT_FOREIGNKEY;
89372
- }
89373
- return SQLITE_OK;
89812
+ if( (db->nDeferredCons+db->nDeferredImmCons)==0 ) return SQLITE_OK;
89813
+ return vdbeFkError(p);
8937489814
}
8937589815
#endif
8937689816
8937789817
/*
8937889818
** This routine is called the when a VDBE tries to halt. If the VDBE
@@ -89462,11 +89902,11 @@
8946289902
}
8946389903
}
8946489904
8946589905
/* Check for immediate foreign key violations. */
8946689906
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89467
- (void)sqlite3VdbeCheckFk(p, 0);
89907
+ (void)sqlite3VdbeCheckFkImmediate(p);
8946889908
}
8946989909
8947089910
/* If the auto-commit flag is set and this is the only active writer
8947189911
** VM, then we do either a commit or rollback of the current transaction.
8947289912
**
@@ -89476,11 +89916,11 @@
8947689916
if( !sqlite3VtabInSync(db)
8947789917
&& db->autoCommit
8947889918
&& db->nVdbeWrite==(p->readOnly==0)
8947989919
){
8948089920
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89481
- rc = sqlite3VdbeCheckFk(p, 1);
89921
+ rc = sqlite3VdbeCheckFkDeferred(p);
8948289922
if( rc!=SQLITE_OK ){
8948389923
if( NEVER(p->readOnly) ){
8948489924
sqlite3VdbeLeave(p);
8948589925
return SQLITE_ERROR;
8948689926
}
@@ -90341,19 +90781,19 @@
9034190781
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
9034290782
pMem->szMalloc = 0;
9034390783
pMem->z = 0;
9034490784
sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
9034590785
d += sqlite3VdbeSerialTypeLen(serial_type);
90346
- pMem++;
9034790786
if( (++u)>=p->nField ) break;
90787
+ pMem++;
9034890788
}
9034990789
if( d>(u32)nKey && u ){
9035090790
assert( CORRUPT_DB );
9035190791
/* In a corrupt record entry, the last pMem might have been set up using
9035290792
** uninitialized memory. Overwrite its value with NULL, to prevent
9035390793
** warnings from MSAN. */
90354
- sqlite3VdbeMemSetNull(pMem-1);
90794
+ sqlite3VdbeMemSetNull(pMem-(u<p->nField));
9035590795
}
9035690796
testcase( u == pKeyInfo->nKeyField + 1 );
9035790797
testcase( u < pKeyInfo->nKeyField + 1 );
9035890798
assert( u<=pKeyInfo->nKeyField + 1 );
9035990799
p->nField = u;
@@ -90520,10 +90960,36 @@
9052090960
** Both *pMem1 and *pMem2 contain string values. Compare the two values
9052190961
** using the collation sequence pColl. As usual, return a negative , zero
9052290962
** or positive value if *pMem1 is less than, equal to or greater than
9052390963
** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
9052490964
*/
90965
+static SQLITE_NOINLINE int vdbeCompareMemStringWithEncodingChange(
90966
+ const Mem *pMem1,
90967
+ const Mem *pMem2,
90968
+ const CollSeq *pColl,
90969
+ u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
90970
+){
90971
+ int rc;
90972
+ const void *v1, *v2;
90973
+ Mem c1;
90974
+ Mem c2;
90975
+ sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90976
+ sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90977
+ sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90978
+ sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90979
+ v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90980
+ v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90981
+ if( (v1==0 || v2==0) ){
90982
+ if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90983
+ rc = 0;
90984
+ }else{
90985
+ rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90986
+ }
90987
+ sqlite3VdbeMemReleaseMalloc(&c1);
90988
+ sqlite3VdbeMemReleaseMalloc(&c2);
90989
+ return rc;
90990
+}
9052590991
static int vdbeCompareMemString(
9052690992
const Mem *pMem1,
9052790993
const Mem *pMem2,
9052890994
const CollSeq *pColl,
9052990995
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
@@ -90531,29 +90997,11 @@
9053190997
if( pMem1->enc==pColl->enc ){
9053290998
/* The strings are already in the correct encoding. Call the
9053390999
** comparison function directly */
9053491000
return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
9053591001
}else{
90536
- int rc;
90537
- const void *v1, *v2;
90538
- Mem c1;
90539
- Mem c2;
90540
- sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90541
- sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90542
- sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90543
- sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90544
- v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90545
- v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90546
- if( (v1==0 || v2==0) ){
90547
- if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90548
- rc = 0;
90549
- }else{
90550
- rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90551
- }
90552
- sqlite3VdbeMemReleaseMalloc(&c1);
90553
- sqlite3VdbeMemReleaseMalloc(&c2);
90554
- return rc;
91002
+ return vdbeCompareMemStringWithEncodingChange(pMem1,pMem2,pColl,prcErr);
9055591003
}
9055691004
}
9055791005
9055891006
/*
9055991007
** The input pBlob is guaranteed to be a Blob that is not marked
@@ -91607,11 +92055,11 @@
9160792055
9160892056
preupdate.v = v;
9160992057
preupdate.pCsr = pCsr;
9161092058
preupdate.op = op;
9161192059
preupdate.iNewReg = iReg;
91612
- preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
92060
+ preupdate.pKeyinfo = &preupdate.uKey.sKey;
9161392061
preupdate.pKeyinfo->db = db;
9161492062
preupdate.pKeyinfo->enc = ENC(db);
9161592063
preupdate.pKeyinfo->nKeyField = pTab->nCol;
9161692064
preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
9161792065
preupdate.iKey1 = iKey1;
@@ -91641,10 +92089,21 @@
9164192089
sqlite3DbFree(db, preupdate.apDflt);
9164292090
}
9164392091
}
9164492092
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
9164592093
92094
+#ifdef SQLITE_ENABLE_PERCENTILE
92095
+/*
92096
+** Return the name of an SQL function associated with the sqlite3_context.
92097
+*/
92098
+SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context *pCtx){
92099
+ assert( pCtx!=0 );
92100
+ assert( pCtx->pFunc!=0 );
92101
+ return pCtx->pFunc->zName;
92102
+}
92103
+#endif /* SQLITE_ENABLE_PERCENTILE */
92104
+
9164692105
/************** End of vdbeaux.c *********************************************/
9164792106
/************** Begin file vdbeapi.c *****************************************/
9164892107
/*
9164992108
** 2004 May 26
9165092109
**
@@ -93338,12 +93797,16 @@
9333893797
if( rc==SQLITE_OK ){
9333993798
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
9334093799
if( zData!=0 ){
9334193800
pVar = &p->aVar[i-1];
9334293801
rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
93343
- if( rc==SQLITE_OK && encoding!=0 ){
93344
- rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
93802
+ if( rc==SQLITE_OK ){
93803
+ if( encoding==0 ){
93804
+ pVar->enc = ENC(p->db);
93805
+ }else{
93806
+ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
93807
+ }
9334593808
}
9334693809
if( rc ){
9334793810
sqlite3Error(p->db, rc);
9334893811
rc = sqlite3ApiExit(p->db, rc);
9334993812
}
@@ -95247,11 +95710,11 @@
9524795710
** to run faster.
9524895711
*/
9524995712
static SQLITE_NOINLINE int vdbeColumnFromOverflow(
9525095713
VdbeCursor *pC, /* The BTree cursor from which we are reading */
9525195714
int iCol, /* The column to read */
95252
- int t, /* The serial-type code for the column value */
95715
+ u32 t, /* The serial-type code for the column value */
9525395716
i64 iOffset, /* Offset to the start of the content value */
9525495717
u32 cacheStatus, /* Current Vdbe.cacheCtr value */
9525595718
u32 colCacheCtr, /* Current value of the column cache counter */
9525695719
Mem *pDest /* Store the value into this register. */
9525795720
){
@@ -96256,11 +96719,11 @@
9625696719
** exits. This opcode is used to raise foreign key constraint errors prior
9625796720
** to returning results such as a row change count or the result of a
9625896721
** RETURNING clause.
9625996722
*/
9626096723
case OP_FkCheck: {
96261
- if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
96724
+ if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){
9626296725
goto abort_due_to_error;
9626396726
}
9626496727
break;
9626596728
}
9626696729
@@ -96348,11 +96811,12 @@
9634896811
flags2 = pIn2->flags & ~MEM_Str;
9634996812
}else if( (flags2 & MEM_Zero)!=0 ){
9635096813
if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
9635196814
flags2 = pIn2->flags & ~MEM_Str;
9635296815
}
96353
- nByte = pIn1->n + pIn2->n;
96816
+ nByte = pIn1->n;
96817
+ nByte += pIn2->n;
9635496818
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
9635596819
goto too_big;
9635696820
}
9635796821
if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
9635896822
goto no_mem;
@@ -98173,11 +98637,11 @@
9817398637
assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
9817498638
assert( pRec->n>=0 );
9817598639
len = (u32)pRec->n;
9817698640
serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
9817798641
if( pRec->flags & MEM_Zero ){
98178
- serial_type += pRec->u.nZero*2;
98642
+ serial_type += (u32)pRec->u.nZero*2;
9817998643
if( nData ){
9818098644
if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
9818198645
len += pRec->u.nZero;
9818298646
}else{
9818398647
nZero += pRec->u.nZero;
@@ -98440,11 +98904,11 @@
9844098904
** and this is a RELEASE command, then the current transaction
9844198905
** is committed.
9844298906
*/
9844398907
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
9844498908
if( isTransaction && p1==SAVEPOINT_RELEASE ){
98445
- if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98909
+ if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
9844698910
goto vdbe_return;
9844798911
}
9844898912
db->autoCommit = 1;
9844998913
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
9845098914
p->pc = (int)(pOp - aOp);
@@ -98558,11 +99022,11 @@
9855899022
*/
9855999023
sqlite3VdbeError(p, "cannot commit transaction - "
9856099024
"SQL statements in progress");
9856199025
rc = SQLITE_BUSY;
9856299026
goto abort_due_to_error;
98563
- }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
99027
+ }else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
9856499028
goto vdbe_return;
9856599029
}else{
9856699030
db->autoCommit = (u8)desiredAutoCommit;
9856799031
}
9856899032
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
@@ -102490,10 +102954,11 @@
102490102954
aRes[1] = aRes[2] = -1;
102491102955
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
102492102956
|| pOp->p2==SQLITE_CHECKPOINT_FULL
102493102957
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
102494102958
|| pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
102959
+ || pOp->p2==SQLITE_CHECKPOINT_NOOP
102495102960
);
102496102961
rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
102497102962
if( rc ){
102498102963
if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
102499102964
rc = SQLITE_OK;
@@ -104689,10 +105154,11 @@
104689105154
UnpackedRecord *pUnpacked; /* Space to unpack a record */
104690105155
SorterList list; /* List for thread to write to a PMA */
104691105156
SorterCompare xCompare; /* Compare function to use */
104692105157
SorterFile file; /* Temp file for level-0 PMAs */
104693105158
SorterFile file2; /* Space for other PMAs */
105159
+ u64 nSpill; /* Total bytes written by this task */
104694105160
};
104695105161
104696105162
104697105163
/*
104698105164
** Main sorter structure. A single instance of this is allocated for each
@@ -104809,10 +105275,11 @@
104809105275
int nBuffer; /* Size of write buffer in bytes */
104810105276
int iBufStart; /* First byte of buffer to write */
104811105277
int iBufEnd; /* Last byte of buffer to write */
104812105278
i64 iWriteOff; /* Offset of start of buffer in file */
104813105279
sqlite3_file *pFd; /* File handle to write to */
105280
+ u64 nPmaSpill; /* Total number of bytes written */
104814105281
};
104815105282
104816105283
/*
104817105284
** This object is the header on a single record while that record is being
104818105285
** held in memory and prior to being written out as part of a PMA.
@@ -105667,10 +106134,16 @@
105667106134
SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
105668106135
VdbeSorter *pSorter;
105669106136
assert( pCsr->eCurType==CURTYPE_SORTER );
105670106137
pSorter = pCsr->uc.pSorter;
105671106138
if( pSorter ){
106139
+ /* Increment db->nSpill by the total number of bytes of data written
106140
+ ** to temp files by this sort operation. */
106141
+ int ii;
106142
+ for(ii=0; ii<pSorter->nTask; ii++){
106143
+ db->nSpill += pSorter->aTask[ii].nSpill;
106144
+ }
105672106145
sqlite3VdbeSorterReset(db, pSorter);
105673106146
sqlite3_free(pSorter->list.aMemory);
105674106147
sqlite3DbFree(db, pSorter);
105675106148
pCsr->uc.pSorter = 0;
105676106149
}
@@ -105892,10 +106365,11 @@
105892106365
if( p->iBufEnd==p->nBuffer ){
105893106366
p->eFWErr = sqlite3OsWrite(p->pFd,
105894106367
&p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
105895106368
p->iWriteOff + p->iBufStart
105896106369
);
106370
+ p->nPmaSpill += (p->iBufEnd - p->iBufStart);
105897106371
p->iBufStart = p->iBufEnd = 0;
105898106372
p->iWriteOff += p->nBuffer;
105899106373
}
105900106374
assert( p->iBufEnd<p->nBuffer );
105901106375
@@ -105908,21 +106382,24 @@
105908106382
** The results of using the PMA-writer after this call are undefined.
105909106383
** Return SQLITE_OK if flushing the buffered data succeeds or is not
105910106384
** required. Otherwise, return an SQLite error code.
105911106385
**
105912106386
** Before returning, set *piEof to the offset immediately following the
105913
-** last byte written to the file.
106387
+** last byte written to the file. Also, increment (*pnSpill) by the total
106388
+** number of bytes written to the file.
105914106389
*/
105915
-static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){
106390
+static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof, u64 *pnSpill){
105916106391
int rc;
105917106392
if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
105918106393
p->eFWErr = sqlite3OsWrite(p->pFd,
105919106394
&p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
105920106395
p->iWriteOff + p->iBufStart
105921106396
);
106397
+ p->nPmaSpill += (p->iBufEnd - p->iBufStart);
105922106398
}
105923106399
*piEof = (p->iWriteOff + p->iBufEnd);
106400
+ *pnSpill += p->nPmaSpill;
105924106401
sqlite3_free(p->aBuffer);
105925106402
rc = p->eFWErr;
105926106403
memset(p, 0, sizeof(PmaWriter));
105927106404
return rc;
105928106405
}
@@ -105998,11 +106475,11 @@
105998106475
vdbePmaWriteVarint(&writer, p->nVal);
105999106476
vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal);
106000106477
if( pList->aMemory==0 ) sqlite3_free(p);
106001106478
}
106002106479
pList->pList = p;
106003
- rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof);
106480
+ rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof, &pTask->nSpill);
106004106481
}
106005106482
106006106483
vdbeSorterWorkDebug(pTask, "exit");
106007106484
assert( rc!=SQLITE_OK || pList->pList==0 );
106008106485
assert( rc!=SQLITE_OK || pTask->file.iEof==iSz );
@@ -106312,11 +106789,11 @@
106312106789
vdbePmaWriteBlob(&writer, pReader->aKey, nKey);
106313106790
assert( pIncr->pMerger->pTask==pTask );
106314106791
rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy);
106315106792
}
106316106793
106317
- rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof);
106794
+ rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof, &pTask->nSpill);
106318106795
if( rc==SQLITE_OK ) rc = rc2;
106319106796
vdbeSorterPopulateDebug(pTask, "exit");
106320106797
return rc;
106321106798
}
106322106799
@@ -109256,12 +109733,12 @@
109256109733
assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
109257109734
if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
109258109735
109259109736
/*
109260109737
** Expression p should encode a floating point value between 1.0 and 0.0.
109261
-** Return 1024 times this value. Or return -1 if p is not a floating point
109262
-** value between 1.0 and 0.0.
109738
+** Return 134,217,728 (2^27) times this value. Or return -1 if p is not
109739
+** a floating point value between 1.0 and 0.0.
109263109740
*/
109264109741
static int exprProbability(Expr *p){
109265109742
double r = -1.0;
109266109743
if( p->op!=TK_FLOAT ) return -1;
109267109744
assert( !ExprHasProperty(p, EP_IntValue) );
@@ -110613,18 +111090,21 @@
110613111090
ExprList *pList /* Expression list to resolve. May be NULL. */
110614111091
){
110615111092
SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
110616111093
NameContext sNC; /* Name context for pParse->pNewTable */
110617111094
int rc;
110618
- u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
111095
+ union {
111096
+ SrcList sSrc;
111097
+ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
111098
+ } uSrc;
110619111099
110620111100
assert( type==0 || pTab!=0 );
110621111101
assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
110622111102
|| type==NC_GenCol || pTab==0 );
110623111103
memset(&sNC, 0, sizeof(sNC));
110624
- pSrc = (SrcList*)srcSpace;
110625
- memset(pSrc, 0, SZ_SRCLIST_1);
111104
+ memset(&uSrc, 0, sizeof(uSrc));
111105
+ pSrc = &uSrc.sSrc;
110626111106
if( pTab ){
110627111107
pSrc->nSrc = 1;
110628111108
pSrc->a[0].zName = pTab->zName;
110629111109
pSrc->a[0].pSTab = pTab;
110630111110
pSrc->a[0].iCursor = -1;
@@ -111883,10 +112363,15 @@
111883112363
if( IsWindowFunc(pExpr) ){
111884112364
sqlite3ExprOrderByAggregateError(pParse, pExpr);
111885112365
sqlite3ExprListDelete(db, pOrderBy);
111886112366
return;
111887112367
}
112368
+ if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
112369
+ sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
112370
+ sqlite3ExprListDelete(db, pOrderBy);
112371
+ return;
112372
+ }
111888112373
111889112374
pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
111890112375
if( pOB==0 ){
111891112376
sqlite3ExprListDelete(db, pOrderBy);
111892112377
return;
@@ -113079,13 +113564,12 @@
113079113564
}
113080113565
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
113081113566
if( addrIsNull==0 ){
113082113567
/*
113083113568
** If the right operand contains a subquery and the left operand does not
113084
- ** and the left operand might be NULL, then check the left operand do
113085
- ** an IsNull check on the left operand before computing the right
113086
- ** operand.
113569
+ ** and the left operand might be NULL, then do an IsNull check
113570
+ ** check on the left operand before computing the right operand.
113087113571
*/
113088113572
if( ExprHasProperty(pExpr->pRight, EP_Subquery)
113089113573
&& sqlite3ExprCanBeNull(pExpr->pLeft)
113090113574
){
113091113575
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r1);
@@ -114645,11 +115129,10 @@
114645115129
int destIfNull /* Jump here if the results are unknown due to NULLs */
114646115130
){
114647115131
int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
114648115132
int eType; /* Type of the RHS */
114649115133
int rLhs; /* Register(s) holding the LHS values */
114650
- int rLhsOrig; /* LHS values prior to reordering by aiMap[] */
114651115134
Vdbe *v; /* Statement under construction */
114652115135
int *aiMap = 0; /* Map from vector field to index column */
114653115136
char *zAff = 0; /* Affinity string for comparisons */
114654115137
int nVector; /* Size of vectors for this IN operator */
114655115138
int iDummy; /* Dummy parameter to exprCodeVector() */
@@ -114708,23 +115191,12 @@
114708115191
** Avoid factoring the LHS of the IN(...) expression out of the loop,
114709115192
** even if it is constant, as OP_Affinity may be used on the register
114710115193
** by code generated below. */
114711115194
assert( pParse->okConstFactor==okConstFactor );
114712115195
pParse->okConstFactor = 0;
114713
- rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
115196
+ rLhs = exprCodeVector(pParse, pLeft, &iDummy);
114714115197
pParse->okConstFactor = okConstFactor;
114715
- for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
114716
- if( i==nVector ){
114717
- /* LHS fields are not reordered */
114718
- rLhs = rLhsOrig;
114719
- }else{
114720
- /* Need to reorder the LHS fields according to aiMap */
114721
- rLhs = sqlite3GetTempRange(pParse, nVector);
114722
- for(i=0; i<nVector; i++){
114723
- sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
114724
- }
114725
- }
114726115198
114727115199
/* If sqlite3FindInIndex() did not find or create an index that is
114728115200
** suitable for evaluating the IN operator, then evaluate using a
114729115201
** sequence of comparisons.
114730115202
**
@@ -114735,10 +115207,11 @@
114735115207
CollSeq *pColl;
114736115208
int labelOk = sqlite3VdbeMakeLabel(pParse);
114737115209
int r2, regToFree;
114738115210
int regCkNull = 0;
114739115211
int ii;
115212
+ assert( nVector==1 );
114740115213
assert( ExprUseXList(pExpr) );
114741115214
pList = pExpr->x.pList;
114742115215
pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
114743115216
if( destIfNull!=destIfFalse ){
114744115217
regCkNull = sqlite3GetTempReg(pParse);
@@ -114775,10 +115248,30 @@
114775115248
}
114776115249
sqlite3VdbeResolveLabel(v, labelOk);
114777115250
sqlite3ReleaseTempReg(pParse, regCkNull);
114778115251
goto sqlite3ExprCodeIN_finished;
114779115252
}
115253
+
115254
+ if( eType!=IN_INDEX_ROWID ){
115255
+ /* If this IN operator will use an index, then the order of columns in the
115256
+ ** vector might be different from the order in the index. In that case,
115257
+ ** we need to reorder the LHS values to be in index order. Run Affinity
115258
+ ** before reordering the columns, so that the affinity is correct.
115259
+ */
115260
+ sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
115261
+ for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
115262
+ if( i!=nVector ){
115263
+ /* Need to reorder the LHS fields according to aiMap */
115264
+ int rLhsOrig = rLhs;
115265
+ rLhs = sqlite3GetTempRange(pParse, nVector);
115266
+ for(i=0; i<nVector; i++){
115267
+ sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
115268
+ }
115269
+ sqlite3ReleaseTempReg(pParse, rLhsOrig);
115270
+ }
115271
+ }
115272
+
114780115273
114781115274
/* Step 2: Check to see if the LHS contains any NULL columns. If the
114782115275
** LHS does contain NULLs then the result must be either FALSE or NULL.
114783115276
** We will then skip the binary search of the RHS.
114784115277
*/
@@ -114802,15 +115295,15 @@
114802115295
*/
114803115296
if( eType==IN_INDEX_ROWID ){
114804115297
/* In this case, the RHS is the ROWID of table b-tree and so we also
114805115298
** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
114806115299
** into a single opcode. */
115300
+ assert( nVector==1 );
114807115301
sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs);
114808115302
VdbeCoverage(v);
114809115303
addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
114810115304
}else{
114811
- sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
114812115305
if( destIfFalse==destIfNull ){
114813115306
/* Combine Step 3 and Step 5 into a single opcode */
114814115307
if( ExprHasProperty(pExpr, EP_Subrtn) ){
114815115308
const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
114816115309
assert( pOp->opcode==OP_Once || pParse->nErr );
@@ -114884,11 +115377,10 @@
114884115377
114885115378
/* Jumps here in order to return true. */
114886115379
sqlite3VdbeJumpHere(v, addrTruthOp);
114887115380
114888115381
sqlite3ExprCodeIN_finished:
114889
- if( rLhs!=rLhsOrig ) sqlite3ReleaseTempReg(pParse, rLhs);
114890115382
VdbeComment((v, "end IN expr"));
114891115383
sqlite3ExprCodeIN_oom_error:
114892115384
sqlite3DbFree(pParse->db, aiMap);
114893115385
sqlite3DbFree(pParse->db, zAff);
114894115386
}
@@ -115442,10 +115934,84 @@
115442115934
}
115443115935
}
115444115936
return 0;
115445115937
}
115446115938
115939
+/*
115940
+** Generate code that evaluates an AND or OR operator leaving a
115941
+** boolean result in a register. pExpr is the AND/OR expression.
115942
+** Store the result in the "target" register. Use short-circuit
115943
+** evaluation to avoid computing both operands, if possible.
115944
+**
115945
+** The code generated might require the use of a temporary register.
115946
+** If it does, then write the number of that temporary register
115947
+** into *pTmpReg. If not, leave *pTmpReg unchanged.
115948
+*/
115949
+static SQLITE_NOINLINE int exprCodeTargetAndOr(
115950
+ Parse *pParse, /* Parsing context */
115951
+ Expr *pExpr, /* AND or OR expression to be coded */
115952
+ int target, /* Put result in this register, guaranteed */
115953
+ int *pTmpReg /* Write a temporary register here */
115954
+){
115955
+ int op; /* The opcode. TK_AND or TK_OR */
115956
+ int skipOp; /* Opcode for the branch that skips one operand */
115957
+ int addrSkip; /* Branch instruction that skips one of the operands */
115958
+ int regSS = 0; /* Register holding computed operand when other omitted */
115959
+ int r1, r2; /* Registers for left and right operands, respectively */
115960
+ Expr *pAlt; /* Alternative, simplified expression */
115961
+ Vdbe *v; /* statement being coded */
115962
+
115963
+ assert( pExpr!=0 );
115964
+ op = pExpr->op;
115965
+ assert( op==TK_AND || op==TK_OR );
115966
+ assert( TK_AND==OP_And ); testcase( op==TK_AND );
115967
+ assert( TK_OR==OP_Or ); testcase( op==TK_OR );
115968
+ assert( pParse->pVdbe!=0 );
115969
+ v = pParse->pVdbe;
115970
+ pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
115971
+ if( pAlt!=pExpr ){
115972
+ r1 = sqlite3ExprCodeTarget(pParse, pAlt, target);
115973
+ sqlite3VdbeAddOp3(v, OP_BitAnd, r1, r1, target);
115974
+ return target;
115975
+ }
115976
+ skipOp = op==TK_AND ? OP_IfNot : OP_If;
115977
+ if( exprEvalRhsFirst(pExpr) ){
115978
+ /* Compute the right operand first. Skip the computation of the left
115979
+ ** operand if the right operand fully determines the result */
115980
+ r2 = regSS = sqlite3ExprCodeTarget(pParse, pExpr->pRight, target);
115981
+ addrSkip = sqlite3VdbeAddOp1(v, skipOp, r2);
115982
+ VdbeComment((v, "skip left operand"));
115983
+ VdbeCoverage(v);
115984
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pTmpReg);
115985
+ }else{
115986
+ /* Compute the left operand first */
115987
+ r1 = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
115988
+ if( ExprHasProperty(pExpr->pRight, EP_Subquery) ){
115989
+ /* Skip over the computation of the right operand if the right
115990
+ ** operand is a subquery and the left operand completely determines
115991
+ ** the result */
115992
+ regSS = r1;
115993
+ addrSkip = sqlite3VdbeAddOp1(v, skipOp, r1);
115994
+ VdbeComment((v, "skip right operand"));
115995
+ VdbeCoverage(v);
115996
+ }else{
115997
+ addrSkip = regSS = 0;
115998
+ }
115999
+ r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pTmpReg);
116000
+ }
116001
+ sqlite3VdbeAddOp3(v, op, r2, r1, target);
116002
+ testcase( (*pTmpReg)==0 );
116003
+ if( addrSkip ){
116004
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
116005
+ sqlite3VdbeJumpHere(v, addrSkip);
116006
+ sqlite3VdbeAddOp3(v, OP_Or, regSS, regSS, target);
116007
+ VdbeComment((v, "short-circut value"));
116008
+ }
116009
+ return target;
116010
+}
116011
+
116012
+
115447116013
115448116014
/*
115449116015
** Generate code into the current Vdbe to evaluate the given
115450116016
** expression. Attempt to store the results in register "target".
115451116017
** Return the register where results are stored.
@@ -115633,10 +116199,16 @@
115633116199
case TK_STRING: {
115634116200
assert( !ExprHasProperty(pExpr, EP_IntValue) );
115635116201
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
115636116202
return target;
115637116203
}
116204
+ case TK_NULLS: {
116205
+ /* Set a range of registers to NULL. pExpr->y.nReg registers starting
116206
+ ** with target */
116207
+ sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
116208
+ return target;
116209
+ }
115638116210
default: {
115639116211
/* Make NULL the default case so that if a bug causes an illegal
115640116212
** Expr node to be passed into this function, it will be handled
115641116213
** sanely and not crash. But keep the assert() to bring the problem
115642116214
** to the attention of the developers. */
@@ -115724,16 +116296,18 @@
115724116296
sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
115725116297
}
115726116298
}
115727116299
testcase( regFree1==0 );
115728116300
testcase( regFree2==0 );
115729
-
115730116301
}
115731116302
break;
115732116303
}
115733116304
case TK_AND:
115734
- case TK_OR:
116305
+ case TK_OR: {
116306
+ inReg = exprCodeTargetAndOr(pParse, pExpr, target, &regFree1);
116307
+ break;
116308
+ }
115735116309
case TK_PLUS:
115736116310
case TK_STAR:
115737116311
case TK_MINUS:
115738116312
case TK_REM:
115739116313
case TK_BITAND:
@@ -115741,12 +116315,10 @@
115741116315
case TK_SLASH:
115742116316
case TK_LSHIFT:
115743116317
case TK_RSHIFT:
115744116318
case TK_CONCAT: {
115745116319
int addrIsNull;
115746
- assert( TK_AND==OP_And ); testcase( op==TK_AND );
115747
- assert( TK_OR==OP_Or ); testcase( op==TK_OR );
115748116320
assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
115749116321
assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS );
115750116322
assert( TK_REM==OP_Remainder ); testcase( op==TK_REM );
115751116323
assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND );
115752116324
assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR );
@@ -116341,10 +116913,29 @@
116341116913
}
116342116914
pParse->pConstExpr = p;
116343116915
}
116344116916
return regDest;
116345116917
}
116918
+
116919
+/*
116920
+** Make arrangements to invoke OP_Null on a range of registers
116921
+** during initialization.
116922
+*/
116923
+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
116924
+ Parse *pParse, /* Parsing context */
116925
+ int iReg, /* First register to set to NULL */
116926
+ int nReg /* Number of sequential registers to NULL out */
116927
+){
116928
+ u8 okConstFactor = pParse->okConstFactor;
116929
+ Expr t;
116930
+ memset(&t, 0, sizeof(t));
116931
+ t.op = TK_NULLS;
116932
+ t.y.nReg = nReg;
116933
+ pParse->okConstFactor = 1;
116934
+ sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
116935
+ pParse->okConstFactor = okConstFactor;
116936
+}
116346116937
116347116938
/*
116348116939
** Generate code to evaluate an expression and store the results
116349116940
** into a register. Return the register number where the results
116350116941
** are stored.
@@ -123872,10 +124463,20 @@
123872124463
if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
123873124464
Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
123874124465
if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
123875124466
pMod = sqlite3PragmaVtabRegister(db, zName);
123876124467
}
124468
+#ifndef SQLITE_OMIT_JSON
124469
+ if( pMod==0 && sqlite3_strnicmp(zName, "json", 4)==0 ){
124470
+ pMod = sqlite3JsonVtabRegister(db, zName);
124471
+ }
124472
+#endif
124473
+#ifdef SQLITE_ENABLE_CARRAY
124474
+ if( pMod==0 && sqlite3_stricmp(zName, "carray")==0 ){
124475
+ pMod = sqlite3CarrayRegister(db);
124476
+ }
124477
+#endif
123877124478
if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
123878124479
testcase( pMod->pEpoTab==0 );
123879124480
return pMod->pEpoTab;
123880124481
}
123881124482
}
@@ -124510,11 +125111,11 @@
124510125111
*/
124511125112
SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
124512125113
int i;
124513125114
i16 iCol16;
124514125115
assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
124515
- assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 );
125116
+ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN*2 );
124516125117
iCol16 = iCol;
124517125118
for(i=0; i<pIdx->nColumn; i++){
124518125119
if( iCol16==pIdx->aiColumn[i] ){
124519125120
return i;
124520125121
}
@@ -124807,10 +125408,13 @@
124807125408
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
124808125409
sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
124809125410
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
124810125411
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
124811125412
sqlite3VdbeAddOp0(v, OP_Close);
125413
+ }else if( db->init.imposterTable ){
125414
+ pTable->tabFlags |= TF_Imposter;
125415
+ if( db->init.imposterTable>=2 ) pTable->tabFlags |= TF_Readonly;
124812125416
}
124813125417
124814125418
/* Normal (non-error) return. */
124815125419
return;
124816125420
@@ -129102,18 +129706,23 @@
129102129706
pKey->aSortFlags[i] = pIdx->aSortOrder[i];
129103129707
assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
129104129708
}
129105129709
if( pParse->nErr ){
129106129710
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
129107
- if( pIdx->bNoQuery==0 ){
129711
+ if( pIdx->bNoQuery==0
129712
+ && sqlite3HashFind(&pIdx->pSchema->idxHash, pIdx->zName)
129713
+ ){
129108129714
/* Deactivate the index because it contains an unknown collating
129109129715
** sequence. The only way to reactive the index is to reload the
129110129716
** schema. Adding the missing collating sequence later does not
129111129717
** reactive the index. The application had the chance to register
129112129718
** the missing index using the collation-needed callback. For
129113129719
** simplicity, SQLite will not give the application a second chance.
129114
- */
129720
+ **
129721
+ ** Except, do not do this if the index is not in the schema hash
129722
+ ** table. In this case the index is currently being constructed
129723
+ ** by a CREATE INDEX statement, and retrying will not help. */
129115129724
pIdx->bNoQuery = 1;
129116129725
pParse->rc = SQLITE_ERROR_RETRY;
129117129726
}
129118129727
sqlite3KeyInfoUnref(pKey);
129119129728
pKey = 0;
@@ -131977,11 +132586,11 @@
131977132586
** a decoding of those digits into *pVal. Or return false if any
131978132587
** one of the first N characters in z[] is not a hexadecimal digit.
131979132588
*/
131980132589
static int isNHex(const char *z, int N, u32 *pVal){
131981132590
int i;
131982
- int v = 0;
132591
+ u32 v = 0;
131983132592
for(i=0; i<N; i++){
131984132593
if( !sqlite3Isxdigit(z[i]) ) return 0;
131985132594
v = (v<<4) + sqlite3HexToInt(z[i]);
131986132595
}
131987132596
*pVal = v;
@@ -132490,10 +133099,11 @@
132490133099
int nSep,
132491133100
const char *zSep
132492133101
){
132493133102
i64 j, n = 0;
132494133103
int i;
133104
+ int bNotNull = 0; /* True after at least NOT NULL argument seen */
132495133105
char *z;
132496133106
for(i=0; i<argc; i++){
132497133107
n += sqlite3_value_bytes(argv[i]);
132498133108
}
132499133109
n += (argc-1)*(i64)nSep;
@@ -132506,16 +133116,17 @@
132506133116
for(i=0; i<argc; i++){
132507133117
if( sqlite3_value_type(argv[i])!=SQLITE_NULL ){
132508133118
int k = sqlite3_value_bytes(argv[i]);
132509133119
const char *v = (const char*)sqlite3_value_text(argv[i]);
132510133120
if( v!=0 ){
132511
- if( j>0 && nSep>0 ){
133121
+ if( bNotNull && nSep>0 ){
132512133122
memcpy(&z[j], zSep, nSep);
132513133123
j += nSep;
132514133124
}
132515133125
memcpy(&z[j], v, k);
132516133126
j += k;
133127
+ bNotNull = 1;
132517133128
}
132518133129
}
132519133130
}
132520133131
z[j] = 0;
132521133132
assert( j<=n );
@@ -133456,10 +134067,456 @@
133456134067
type0 = sqlite3_value_numeric_type(argv[0]);
133457134068
if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
133458134069
x = sqlite3_value_double(argv[0]);
133459134070
sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
133460134071
}
134072
+
134073
+#if defined(SQLITE_ENABLE_PERCENTILE)
134074
+/***********************************************************************
134075
+** This section implements the percentile(Y,P) SQL function and similar.
134076
+** Requirements:
134077
+**
134078
+** (1) The percentile(Y,P) function is an aggregate function taking
134079
+** exactly two arguments.
134080
+**
134081
+** (2) If the P argument to percentile(Y,P) is not the same for every
134082
+** row in the aggregate then an error is thrown. The word "same"
134083
+** in the previous sentence means that the value differ by less
134084
+** than 0.001.
134085
+**
134086
+** (3) If the P argument to percentile(Y,P) evaluates to anything other
134087
+** than a number in the range of 0.0 to 100.0 inclusive then an
134088
+** error is thrown.
134089
+**
134090
+** (4) If any Y argument to percentile(Y,P) evaluates to a value that
134091
+** is not NULL and is not numeric then an error is thrown.
134092
+**
134093
+** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
134094
+** infinity then an error is thrown. (SQLite always interprets NaN
134095
+** values as NULL.)
134096
+**
134097
+** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
134098
+** including CASE WHEN expressions.
134099
+**
134100
+** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
134101
+** one million (1,000,000) rows.
134102
+**
134103
+** (8) If there are no non-NULL values for Y, then percentile(Y,P)
134104
+** returns NULL.
134105
+**
134106
+** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
134107
+** returns the one Y value.
134108
+**
134109
+** (10) If there N non-NULL values of Y where N is two or more and
134110
+** the Y values are ordered from least to greatest and a graph is
134111
+** drawn from 0 to N-1 such that the height of the graph at J is
134112
+** the J-th Y value and such that straight lines are drawn between
134113
+** adjacent Y values, then the percentile(Y,P) function returns
134114
+** the height of the graph at P*(N-1)/100.
134115
+**
134116
+** (11) The percentile(Y,P) function always returns either a floating
134117
+** point number or NULL.
134118
+**
134119
+** (12) The percentile(Y,P) is implemented as a single C99 source-code
134120
+** file that compiles into a shared-library or DLL that can be loaded
134121
+** into SQLite using the sqlite3_load_extension() interface.
134122
+**
134123
+** (13) A separate median(Y) function is the equivalent percentile(Y,50).
134124
+**
134125
+** (14) A separate percentile_cont(Y,P) function is equivalent to
134126
+** percentile(Y,P/100.0). In other words, the fraction value in
134127
+** the second argument is in the range of 0 to 1 instead of 0 to 100.
134128
+**
134129
+** (15) A separate percentile_disc(Y,P) function is like
134130
+** percentile_cont(Y,P) except that instead of returning the weighted
134131
+** average of the nearest two input values, it returns the next lower
134132
+** value. So the percentile_disc(Y,P) will always return a value
134133
+** that was one of the inputs.
134134
+**
134135
+** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
134136
+** percentile_disc(Y,P) can be used as window functions.
134137
+**
134138
+** Differences from standard SQL:
134139
+**
134140
+** * The percentile_cont(X,P) function is equivalent to the following in
134141
+** standard SQL:
134142
+**
134143
+** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
134144
+**
134145
+** The SQLite syntax is much more compact. The standard SQL syntax
134146
+** is also supported if SQLite is compiled with the
134147
+** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
134148
+**
134149
+** * No median(X) function exists in the SQL standard. App developers
134150
+** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
134151
+**
134152
+** * No percentile(Y,P) function exists in the SQL standard. Instead of
134153
+** percential(Y,P), developers must write this:
134154
+** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
134155
+** the fraction parameter to percentile() goes from 0 to 100 whereas
134156
+** the fraction parameter in SQL standard percentile_cont() goes from
134157
+** 0 to 1.
134158
+**
134159
+** Implementation notes as of 2024-08-31:
134160
+**
134161
+** * The regular aggregate-function versions of these routines work
134162
+** by accumulating all values in an array of doubles, then sorting
134163
+** that array using quicksort before computing the answer. Thus
134164
+** the runtime is O(NlogN) where N is the number of rows of input.
134165
+**
134166
+** * For the window-function versions of these routines, the array of
134167
+** inputs is sorted as soon as the first value is computed. Thereafter,
134168
+** the array is kept in sorted order using an insert-sort. This
134169
+** results in O(N*K) performance where K is the size of the window.
134170
+** One can imagine alternative implementations that give O(N*logN*logK)
134171
+** performance, but they require more complex logic and data structures.
134172
+** The developers have elected to keep the asymptotically slower
134173
+** algorithm for now, for simplicity, under the theory that window
134174
+** functions are seldom used and when they are, the window size K is
134175
+** often small. The developers might revisit that decision later,
134176
+** should the need arise.
134177
+*/
134178
+
134179
+/* The following object is the group context for a single percentile()
134180
+** aggregate. Remember all input Y values until the very end.
134181
+** Those values are accumulated in the Percentile.a[] array.
134182
+*/
134183
+typedef struct Percentile Percentile;
134184
+struct Percentile {
134185
+ unsigned nAlloc; /* Number of slots allocated for a[] */
134186
+ unsigned nUsed; /* Number of slots actually used in a[] */
134187
+ char bSorted; /* True if a[] is already in sorted order */
134188
+ char bKeepSorted; /* True if advantageous to keep a[] sorted */
134189
+ char bPctValid; /* True if rPct is valid */
134190
+ double rPct; /* Fraction. 0.0 to 1.0 */
134191
+ double *a; /* Array of Y values */
134192
+};
134193
+
134194
+/*
134195
+** Return TRUE if the input floating-point number is an infinity.
134196
+*/
134197
+static int percentIsInfinity(double r){
134198
+ sqlite3_uint64 u;
134199
+ assert( sizeof(u)==sizeof(r) );
134200
+ memcpy(&u, &r, sizeof(u));
134201
+ return ((u>>52)&0x7ff)==0x7ff;
134202
+}
134203
+
134204
+/*
134205
+** Return TRUE if two doubles differ by 0.001 or less.
134206
+*/
134207
+static int percentSameValue(double a, double b){
134208
+ a -= b;
134209
+ return a>=-0.001 && a<=0.001;
134210
+}
134211
+
134212
+/*
134213
+** Search p (which must have p->bSorted) looking for an entry with
134214
+** value y. Return the index of that entry.
134215
+**
134216
+** If bExact is true, return -1 if the entry is not found.
134217
+**
134218
+** If bExact is false, return the index at which a new entry with
134219
+** value y should be insert in order to keep the values in sorted
134220
+** order. The smallest return value in this case will be 0, and
134221
+** the largest return value will be p->nUsed.
134222
+*/
134223
+static int percentBinarySearch(Percentile *p, double y, int bExact){
134224
+ int iFirst = 0; /* First element of search range */
134225
+ int iLast = p->nUsed - 1; /* Last element of search range */
134226
+ while( iLast>=iFirst ){
134227
+ int iMid = (iFirst+iLast)/2;
134228
+ double x = p->a[iMid];
134229
+ if( x<y ){
134230
+ iFirst = iMid + 1;
134231
+ }else if( x>y ){
134232
+ iLast = iMid - 1;
134233
+ }else{
134234
+ return iMid;
134235
+ }
134236
+ }
134237
+ if( bExact ) return -1;
134238
+ return iFirst;
134239
+}
134240
+
134241
+/*
134242
+** Generate an error for a percentile function.
134243
+**
134244
+** The error format string must have exactly one occurrence of "%%s()"
134245
+** (with two '%' characters). That substring will be replaced by the name
134246
+** of the function.
134247
+*/
134248
+static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
134249
+ char *zMsg1;
134250
+ char *zMsg2;
134251
+ va_list ap;
134252
+
134253
+ va_start(ap, zFormat);
134254
+ zMsg1 = sqlite3_vmprintf(zFormat, ap);
134255
+ va_end(ap);
134256
+ zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, sqlite3VdbeFuncName(pCtx)) : 0;
134257
+ sqlite3_result_error(pCtx, zMsg2, -1);
134258
+ sqlite3_free(zMsg1);
134259
+ sqlite3_free(zMsg2);
134260
+}
134261
+
134262
+/*
134263
+** The "step" function for percentile(Y,P) is called once for each
134264
+** input row.
134265
+*/
134266
+static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
134267
+ Percentile *p;
134268
+ double rPct;
134269
+ int eType;
134270
+ double y;
134271
+ assert( argc==2 || argc==1 );
134272
+
134273
+ if( argc==1 ){
134274
+ /* Requirement 13: median(Y) is the same as percentile(Y,50). */
134275
+ rPct = 0.5;
134276
+ }else{
134277
+ /* P must be a number between 0 and 100 for percentile() or between
134278
+ ** 0.0 and 1.0 for percentile_cont() and percentile_disc().
134279
+ **
134280
+ ** The user-data is an integer which is 10 times the upper bound.
134281
+ */
134282
+ double mxFrac = (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx))&2)? 100.0 : 1.0;
134283
+ eType = sqlite3_value_numeric_type(argv[1]);
134284
+ rPct = sqlite3_value_double(argv[1])/mxFrac;
134285
+ if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
134286
+ || rPct<0.0 || rPct>1.0
134287
+ ){
134288
+ percentError(pCtx, "the fraction argument to %%s()"
134289
+ " is not between 0.0 and %.1f",
134290
+ (double)mxFrac);
134291
+ return;
134292
+ }
134293
+ }
134294
+
134295
+ /* Allocate the session context. */
134296
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
134297
+ if( p==0 ) return;
134298
+
134299
+ /* Remember the P value. Throw an error if the P value is different
134300
+ ** from any prior row, per Requirement (2). */
134301
+ if( !p->bPctValid ){
134302
+ p->rPct = rPct;
134303
+ p->bPctValid = 1;
134304
+ }else if( !percentSameValue(p->rPct,rPct) ){
134305
+ percentError(pCtx, "the fraction argument to %%s()"
134306
+ " is not the same for all input rows");
134307
+ return;
134308
+ }
134309
+
134310
+ /* Ignore rows for which Y is NULL */
134311
+ eType = sqlite3_value_type(argv[0]);
134312
+ if( eType==SQLITE_NULL ) return;
134313
+
134314
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
134315
+ ** Requirement 4 */
134316
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
134317
+ percentError(pCtx, "input to %%s() is not numeric");
134318
+ return;
134319
+ }
134320
+
134321
+ /* Throw an error if the Y value is infinity or NaN */
134322
+ y = sqlite3_value_double(argv[0]);
134323
+ if( percentIsInfinity(y) ){
134324
+ percentError(pCtx, "Inf input to %%s()");
134325
+ return;
134326
+ }
134327
+
134328
+ /* Allocate and store the Y */
134329
+ if( p->nUsed>=p->nAlloc ){
134330
+ unsigned n = p->nAlloc*2 + 250;
134331
+ double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
134332
+ if( a==0 ){
134333
+ sqlite3_free(p->a);
134334
+ memset(p, 0, sizeof(*p));
134335
+ sqlite3_result_error_nomem(pCtx);
134336
+ return;
134337
+ }
134338
+ p->nAlloc = n;
134339
+ p->a = a;
134340
+ }
134341
+ if( p->nUsed==0 ){
134342
+ p->a[p->nUsed++] = y;
134343
+ p->bSorted = 1;
134344
+ }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
134345
+ p->a[p->nUsed++] = y;
134346
+ }else if( p->bKeepSorted ){
134347
+ int i;
134348
+ i = percentBinarySearch(p, y, 0);
134349
+ if( i<(int)p->nUsed ){
134350
+ memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
134351
+ }
134352
+ p->a[i] = y;
134353
+ p->nUsed++;
134354
+ }else{
134355
+ p->a[p->nUsed++] = y;
134356
+ p->bSorted = 0;
134357
+ }
134358
+}
134359
+
134360
+/*
134361
+** Interchange two doubles.
134362
+*/
134363
+#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
134364
+
134365
+/*
134366
+** Sort an array of doubles.
134367
+**
134368
+** Algorithm: quicksort
134369
+**
134370
+** This is implemented separately rather than using the qsort() routine
134371
+** from the standard library because:
134372
+**
134373
+** (1) To avoid a dependency on qsort()
134374
+** (2) To avoid the function call to the comparison routine for each
134375
+** comparison.
134376
+*/
134377
+static void percentSort(double *a, unsigned int n){
134378
+ int iLt; /* Entries before a[iLt] are less than rPivot */
134379
+ int iGt; /* Entries at or after a[iGt] are greater than rPivot */
134380
+ int i; /* Loop counter */
134381
+ double rPivot; /* The pivot value */
134382
+
134383
+ assert( n>=2 );
134384
+ if( a[0]>a[n-1] ){
134385
+ SWAP_DOUBLE(a[0],a[n-1])
134386
+ }
134387
+ if( n==2 ) return;
134388
+ iGt = n-1;
134389
+ i = n/2;
134390
+ if( a[0]>a[i] ){
134391
+ SWAP_DOUBLE(a[0],a[i])
134392
+ }else if( a[i]>a[iGt] ){
134393
+ SWAP_DOUBLE(a[i],a[iGt])
134394
+ }
134395
+ if( n==3 ) return;
134396
+ rPivot = a[i];
134397
+ iLt = i = 1;
134398
+ do{
134399
+ if( a[i]<rPivot ){
134400
+ if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
134401
+ iLt++;
134402
+ i++;
134403
+ }else if( a[i]>rPivot ){
134404
+ do{
134405
+ iGt--;
134406
+ }while( iGt>i && a[iGt]>rPivot );
134407
+ SWAP_DOUBLE(a[i],a[iGt])
134408
+ }else{
134409
+ i++;
134410
+ }
134411
+ }while( i<iGt );
134412
+ if( iLt>=2 ) percentSort(a, iLt);
134413
+ if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
134414
+
134415
+/* Uncomment for testing */
134416
+#if 0
134417
+ for(i=0; i<n-1; i++){
134418
+ assert( a[i]<=a[i+1] );
134419
+ }
134420
+#endif
134421
+}
134422
+
134423
+
134424
+/*
134425
+** The "inverse" function for percentile(Y,P) is called to remove a
134426
+** row that was previously inserted by "step".
134427
+*/
134428
+static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
134429
+ Percentile *p;
134430
+ int eType;
134431
+ double y;
134432
+ int i;
134433
+ assert( argc==2 || argc==1 );
134434
+
134435
+ /* Allocate the session context. */
134436
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
134437
+ assert( p!=0 );
134438
+
134439
+ /* Ignore rows for which Y is NULL */
134440
+ eType = sqlite3_value_type(argv[0]);
134441
+ if( eType==SQLITE_NULL ) return;
134442
+
134443
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
134444
+ ** Requirement 4 */
134445
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
134446
+ return;
134447
+ }
134448
+
134449
+ /* Ignore the Y value if it is infinity or NaN */
134450
+ y = sqlite3_value_double(argv[0]);
134451
+ if( percentIsInfinity(y) ){
134452
+ return;
134453
+ }
134454
+ if( p->bSorted==0 ){
134455
+ assert( p->nUsed>1 );
134456
+ percentSort(p->a, p->nUsed);
134457
+ p->bSorted = 1;
134458
+ }
134459
+ p->bKeepSorted = 1;
134460
+
134461
+ /* Find and remove the row */
134462
+ i = percentBinarySearch(p, y, 1);
134463
+ if( i>=0 ){
134464
+ p->nUsed--;
134465
+ if( i<(int)p->nUsed ){
134466
+ memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
134467
+ }
134468
+ }
134469
+}
134470
+
134471
+/*
134472
+** Compute the final output of percentile(). Clean up all allocated
134473
+** memory if and only if bIsFinal is true.
134474
+*/
134475
+static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
134476
+ Percentile *p;
134477
+ int settings = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx))&1; /* Discrete? */
134478
+ unsigned i1, i2;
134479
+ double v1, v2;
134480
+ double ix, vx;
134481
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
134482
+ if( p==0 ) return;
134483
+ if( p->a==0 ) return;
134484
+ if( p->nUsed ){
134485
+ if( p->bSorted==0 ){
134486
+ assert( p->nUsed>1 );
134487
+ percentSort(p->a, p->nUsed);
134488
+ p->bSorted = 1;
134489
+ }
134490
+ ix = p->rPct*(p->nUsed-1);
134491
+ i1 = (unsigned)ix;
134492
+ if( settings & 1 ){
134493
+ vx = p->a[i1];
134494
+ }else{
134495
+ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
134496
+ v1 = p->a[i1];
134497
+ v2 = p->a[i2];
134498
+ vx = v1 + (v2-v1)*(ix-i1);
134499
+ }
134500
+ sqlite3_result_double(pCtx, vx);
134501
+ }
134502
+ if( bIsFinal ){
134503
+ sqlite3_free(p->a);
134504
+ memset(p, 0, sizeof(*p));
134505
+ }else{
134506
+ p->bKeepSorted = 1;
134507
+ }
134508
+}
134509
+static void percentFinal(sqlite3_context *pCtx){
134510
+ percentCompute(pCtx, 1);
134511
+}
134512
+static void percentValue(sqlite3_context *pCtx){
134513
+ percentCompute(pCtx, 0);
134514
+}
134515
+/****** End of percentile family of functions ******/
134516
+#endif /* SQLITE_ENABLE_PERCENTILE */
134517
+
133461134518
133462134519
#ifdef SQLITE_DEBUG
133463134520
/*
133464134521
** Implementation of fpdecode(x,y,z) function.
133465134522
**
@@ -133687,10 +134744,25 @@
133687134744
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
133688134745
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
133689134746
WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
133690134747
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
133691134748
134749
+#ifdef SQLITE_ENABLE_PERCENTILE
134750
+ WAGGREGATE(median, 1, 0,0, percentStep,
134751
+ percentFinal, percentValue, percentInverse,
134752
+ SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134753
+ WAGGREGATE(percentile, 2, 0x2,0, percentStep,
134754
+ percentFinal, percentValue, percentInverse,
134755
+ SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134756
+ WAGGREGATE(percentile_cont, 2, 0,0, percentStep,
134757
+ percentFinal, percentValue, percentInverse,
134758
+ SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134759
+ WAGGREGATE(percentile_disc, 2, 0x1,0, percentStep,
134760
+ percentFinal, percentValue, percentInverse,
134761
+ SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134762
+#endif /* SQLITE_ENABLE_PERCENTILE */
134763
+
133692134764
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
133693134765
#ifdef SQLITE_CASE_SENSITIVE_LIKE
133694134766
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
133695134767
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
133696134768
#else
@@ -139184,10 +140256,14 @@
139184140256
/* Version 3.44.0 and later */
139185140257
void *(*get_clientdata)(sqlite3*,const char*);
139186140258
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
139187140259
/* Version 3.50.0 and later */
139188140260
int (*setlk_timeout)(sqlite3*,int,int);
140261
+ /* Version 3.51.0 and later */
140262
+ int (*set_errmsg)(sqlite3*,int,const char*);
140263
+ int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
140264
+
139189140265
};
139190140266
139191140267
/*
139192140268
** This is the function signature used for all extension entry points. It
139193140269
** is also defined in the file "loadext.c".
@@ -139519,10 +140595,13 @@
139519140595
/* Version 3.44.0 and later */
139520140596
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
139521140597
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
139522140598
/* Version 3.50.0 and later */
139523140599
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
140600
+/* Version 3.51.0 and later */
140601
+#define sqlite3_set_errmsg sqlite3_api->set_errmsg
140602
+#define sqlite3_db_status64 sqlite3_api->db_status64
139524140603
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
139525140604
139526140605
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
139527140606
/* This case when the file really is being compiled as a loadable
139528140607
** extension */
@@ -140042,11 +141121,14 @@
140042141121
sqlite3_stmt_explain,
140043141122
/* Version 3.44.0 and later */
140044141123
sqlite3_get_clientdata,
140045141124
sqlite3_set_clientdata,
140046141125
/* Version 3.50.0 and later */
140047
- sqlite3_setlk_timeout
141126
+ sqlite3_setlk_timeout,
141127
+ /* Version 3.51.0 and later */
141128
+ sqlite3_set_errmsg,
141129
+ sqlite3_db_status64
140048141130
};
140049141131
140050141132
/* True if x is the directory separator character
140051141133
*/
140052141134
#if SQLITE_OS_WIN
@@ -141503,10 +142585,26 @@
141503142585
addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1);
141504142586
VdbeCoverage(v);
141505142587
sqlite3VdbeAddOp0(v, OP_Halt);
141506142588
return addr;
141507142589
}
142590
+
142591
+/*
142592
+** Should table pTab be skipped when doing an integrity_check?
142593
+** Return true or false.
142594
+**
142595
+** If pObjTab is not null, the return true if pTab matches pObjTab.
142596
+**
142597
+** If pObjTab is null, then return true only if pTab is an imposter table.
142598
+*/
142599
+static int tableSkipIntegrityCheck(const Table *pTab, const Table *pObjTab){
142600
+ if( pObjTab ){
142601
+ return pTab!=pObjTab;
142602
+ }else{
142603
+ return (pTab->tabFlags & TF_Imposter)!=0;
142604
+ }
142605
+}
141508142606
141509142607
/*
141510142608
** Process a pragma statement.
141511142609
**
141512142610
** Pragmas are of this form:
@@ -142849,11 +143947,11 @@
142849143947
pTbls = &db->aDb[i].pSchema->tblHash;
142850143948
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
142851143949
Table *pTab = sqliteHashData(x); /* Current table */
142852143950
Index *pIdx; /* An index on pTab */
142853143951
int nIdx; /* Number of indexes on pTab */
142854
- if( pObjTab && pObjTab!=pTab ) continue;
143952
+ if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
142855143953
if( HasRowid(pTab) ) cnt++;
142856143954
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
142857143955
}
142858143956
if( cnt==0 ) continue;
142859143957
if( pObjTab ) cnt++;
@@ -142862,11 +143960,11 @@
142862143960
cnt = 0;
142863143961
if( pObjTab ) aRoot[++cnt] = 0;
142864143962
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
142865143963
Table *pTab = sqliteHashData(x);
142866143964
Index *pIdx;
142867
- if( pObjTab && pObjTab!=pTab ) continue;
143965
+ if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
142868143966
if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
142869143967
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
142870143968
aRoot[++cnt] = pIdx->tnum;
142871143969
}
142872143970
}
@@ -142893,11 +143991,11 @@
142893143991
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
142894143992
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
142895143993
int iTab = 0;
142896143994
Table *pTab = sqliteHashData(x);
142897143995
Index *pIdx;
142898
- if( pObjTab && pObjTab!=pTab ) continue;
143996
+ if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
142899143997
if( HasRowid(pTab) ){
142900143998
iTab = cnt++;
142901143999
}else{
142902144000
iTab = cnt;
142903144001
for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){
@@ -142929,11 +144027,11 @@
142929144027
int r1 = -1;
142930144028
int bStrict; /* True for a STRICT table */
142931144029
int r2; /* Previous key for WITHOUT ROWID tables */
142932144030
int mxCol; /* Maximum non-virtual column number */
142933144031
142934
- if( pObjTab && pObjTab!=pTab ) continue;
144032
+ if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
142935144033
if( !IsOrdinaryTable(pTab) ) continue;
142936144034
if( isQuick || HasRowid(pTab) ){
142937144035
pPk = 0;
142938144036
r2 = 0;
142939144037
}else{
@@ -143253,11 +144351,11 @@
143253144351
*/
143254144352
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
143255144353
Table *pTab = sqliteHashData(x);
143256144354
sqlite3_vtab *pVTab;
143257144355
int a1;
143258
- if( pObjTab && pObjTab!=pTab ) continue;
144356
+ if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
143259144357
if( IsOrdinaryTable(pTab) ) continue;
143260144358
if( !IsVirtual(pTab) ) continue;
143261144359
if( pTab->nCol<=0 ){
143262144360
const char *zMod = pTab->u.vtab.azArg[0];
143263144361
if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
@@ -143485,10 +144583,12 @@
143485144583
eMode = SQLITE_CHECKPOINT_FULL;
143486144584
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
143487144585
eMode = SQLITE_CHECKPOINT_RESTART;
143488144586
}else if( sqlite3StrICmp(zRight, "truncate")==0 ){
143489144587
eMode = SQLITE_CHECKPOINT_TRUNCATE;
144588
+ }else if( sqlite3StrICmp(zRight, "noop")==0 ){
144589
+ eMode = SQLITE_CHECKPOINT_NOOP;
143490144590
}
143491144591
}
143492144592
pParse->nMem = 3;
143493144593
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
143494144594
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
@@ -145051,13 +146151,15 @@
145051146151
** or encounters a permanent error. A schema problem after one schema
145052146152
** reset is considered a permanent error. */
145053146153
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
145054146154
assert( rc==SQLITE_OK || *ppStmt==0 );
145055146155
if( rc==SQLITE_OK || db->mallocFailed ) break;
145056
- }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
145057
- || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
146156
+ cnt++;
146157
+ }while( (rc==SQLITE_ERROR_RETRY && ALWAYS(cnt<=SQLITE_MAX_PREPARE_RETRY))
146158
+ || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt)==1) );
145058146159
sqlite3BtreeLeaveAll(db);
146160
+ assert( rc!=SQLITE_ERROR_RETRY );
145059146161
rc = sqlite3ApiExit(db, rc);
145060146162
assert( (rc&db->errMask)==rc );
145061146163
db->busyHandler.nBusy = 0;
145062146164
sqlite3_mutex_leave(db->mutex);
145063146165
assert( rc==SQLITE_OK || (*ppStmt)==0 );
@@ -145727,12 +146829,11 @@
145727146829
while( p ){
145728146830
ExprSetProperty(p, joinFlag);
145729146831
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
145730146832
ExprSetVVAProperty(p, EP_NoReduce);
145731146833
p->w.iJoin = iTable;
145732
- if( p->op==TK_FUNCTION ){
145733
- assert( ExprUseXList(p) );
146834
+ if( ExprUseXList(p) ){
145734146835
if( p->x.pList ){
145735146836
int i;
145736146837
for(i=0; i<p->x.pList->nExpr; i++){
145737146838
sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
145738146839
}
@@ -145944,10 +147045,11 @@
145944147045
else if( pRight->u3.pOn ){
145945147046
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
145946147047
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
145947147048
pRight->u3.pOn = 0;
145948147049
pRight->fg.isOn = 1;
147050
+ p->selFlags |= SF_OnToWhere;
145949147051
}
145950147052
}
145951147053
return 0;
145952147054
}
145953147055
@@ -146830,11 +147932,14 @@
146830147932
** Allocate a KeyInfo object sufficient for an index of N key columns and
146831147933
** X extra columns.
146832147934
*/
146833147935
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
146834147936
int nExtra = (N+X)*(sizeof(CollSeq*)+1);
146835
- KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
147937
+ KeyInfo *p;
147938
+ assert( X>=0 );
147939
+ if( NEVER(N+X>0xffff) ) return (KeyInfo*)sqlite3OomFault(db);
147940
+ p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
146836147941
if( p ){
146837147942
p->aSortFlags = (u8*)&p->aColl[N+X];
146838147943
p->nKeyField = (u16)N;
146839147944
p->nAllField = (u16)(N+X);
146840147945
p->enc = ENC(db);
@@ -149149,11 +150254,11 @@
149149150254
** expressions in pEList.
149150150255
**
149151150256
** ## About "isOuterJoin":
149152150257
**
149153150258
** The isOuterJoin column indicates that the replacement will occur into a
149154
-** position in the parent that NULL-able due to an OUTER JOIN. Either the
150259
+** position in the parent that is NULL-able due to an OUTER JOIN. Either the
149155150260
** target slot in the parent is the right operand of a LEFT JOIN, or one of
149156150261
** the left operands of a RIGHT JOIN. In either case, we need to potentially
149157150262
** bypass the substituted expression with OP_IfNullRow.
149158150263
**
149159150264
** Suppose the original expression is an integer constant. Even though the table
@@ -149987,21 +151092,16 @@
149987151092
** elements we are now copying in.
149988151093
*/
149989151094
pSub = pSub1;
149990151095
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
149991151096
int nSubSrc;
149992
- u8 jointype = 0;
149993
- u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ;
151097
+ u8 jointype = pSubitem->fg.jointype;
149994151098
assert( pSub!=0 );
149995151099
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
149996151100
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
149997151101
pSrc = pParent->pSrc; /* FROM clause of the outer query */
149998151102
149999
- if( pParent==p ){
150000
- jointype = pSubitem->fg.jointype; /* First time through the loop */
150001
- }
150002
-
150003151103
/* The subquery uses a single slot of the FROM clause of the outer
150004151104
** query. If the subquery has more than one element in its FROM clause,
150005151105
** then expand the outer query to make space for it to hold all elements
150006151106
** of the subquery.
150007151107
**
@@ -150017,10 +151117,11 @@
150017151117
*/
150018151118
if( nSubSrc>1 ){
150019151119
pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1);
150020151120
if( pSrc==0 ) break;
150021151121
pParent->pSrc = pSrc;
151122
+ pSubitem = &pSrc->a[iFrom];
150022151123
}
150023151124
150024151125
/* Transfer the FROM clause terms from the subquery into the
150025151126
** outer query.
150026151127
*/
@@ -150031,15 +151132,14 @@
150031151132
assert( pItem->fg.isSubquery
150032151133
|| pItem->fg.fixedSchema
150033151134
|| pItem->u4.zDatabase==0 );
150034151135
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
150035151136
*pItem = pSubSrc->a[i];
150036
- pItem->fg.jointype |= ltorj;
151137
+ pItem->fg.jointype |= (jointype & JT_LTORJ);
150037151138
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
150038151139
}
150039
- pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
150040
- pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
151140
+ pSubitem->fg.jointype |= jointype;
150041151141
150042151142
/* Now begin substituting subquery result set expressions for
150043151143
** references to the iParent in the outer query.
150044151144
**
150045151145
** Example:
@@ -152746,10 +153846,11 @@
152746153846
Select *pSub = pWhere->x.pSelect;
152747153847
Expr *pSubWhere = pSub->pWhere;
152748153848
if( pSub->pSrc->nSrc==1
152749153849
&& (pSub->selFlags & SF_Aggregate)==0
152750153850
&& !pSub->pSrc->a[0].fg.isSubquery
153851
+ && pSub->pLimit==0
152751153852
){
152752153853
memset(pWhere, 0, sizeof(*pWhere));
152753153854
pWhere->op = TK_INTEGER;
152754153855
pWhere->u.iValue = 1;
152755153856
ExprSetProperty(pWhere, EP_IntValue);
@@ -152774,10 +153875,119 @@
152774153875
existsToJoin(pParse, p, pSubWhere);
152775153876
}
152776153877
}
152777153878
}
152778153879
}
153880
+
153881
+/*
153882
+** Type used for Walker callbacks by selectCheckOnClauses().
153883
+*/
153884
+typedef struct CheckOnCtx CheckOnCtx;
153885
+struct CheckOnCtx {
153886
+ SrcList *pSrc; /* SrcList for this context */
153887
+ int iJoin; /* Cursor numbers must be =< than this */
153888
+ CheckOnCtx *pParent; /* Parent context */
153889
+};
153890
+
153891
+/*
153892
+** True if the SrcList passed as the only argument contains at least
153893
+** one RIGHT or FULL JOIN. False otherwise.
153894
+*/
153895
+#define hasRightJoin(pSrc) (((pSrc)->a[0].fg.jointype & JT_LTORJ)!=0)
153896
+
153897
+/*
153898
+** The xExpr callback for the search of invalid ON clause terms.
153899
+*/
153900
+static int selectCheckOnClausesExpr(Walker *pWalker, Expr *pExpr){
153901
+ CheckOnCtx *pCtx = pWalker->u.pCheckOnCtx;
153902
+
153903
+ /* Check if pExpr is root or near-root of an ON clause constraint that needs
153904
+ ** to be checked to ensure that it does not refer to tables in its FROM
153905
+ ** clause to the right of itself. i.e. it is either:
153906
+ **
153907
+ ** + an ON clause on an OUTER join, or
153908
+ ** + an ON clause on an INNER join within a FROM that features at
153909
+ ** least one RIGHT or FULL join.
153910
+ */
153911
+ if( (ExprHasProperty(pExpr, EP_OuterON))
153912
+ || (ExprHasProperty(pExpr, EP_InnerON) && hasRightJoin(pCtx->pSrc))
153913
+ ){
153914
+ /* If CheckOnCtx.iJoin is already set, then fall through and process
153915
+ ** this expression node as normal. Or, if CheckOnCtx.iJoin is still 0,
153916
+ ** set it to the cursor number of the RHS of the join to which this
153917
+ ** ON expression was attached and then iterate through the entire
153918
+ ** expression. */
153919
+ assert( pCtx->iJoin==0 || pCtx->iJoin==pExpr->w.iJoin );
153920
+ if( pCtx->iJoin==0 ){
153921
+ pCtx->iJoin = pExpr->w.iJoin;
153922
+ sqlite3WalkExprNN(pWalker, pExpr);
153923
+ pCtx->iJoin = 0;
153924
+ return WRC_Prune;
153925
+ }
153926
+ }
153927
+
153928
+ if( pExpr->op==TK_COLUMN ){
153929
+ /* A column expression. Find the SrcList (if any) to which it refers.
153930
+ ** Then, if CheckOnCtx.iJoin indicates that this expression is part of an
153931
+ ** ON clause from that SrcList (i.e. if iJoin is non-zero), check that it
153932
+ ** does not refer to a table to the right of CheckOnCtx.iJoin. */
153933
+ do {
153934
+ SrcList *pSrc = pCtx->pSrc;
153935
+ int iTab = pExpr->iTable;
153936
+ if( iTab>=pSrc->a[0].iCursor && iTab<=pSrc->a[pSrc->nSrc-1].iCursor ){
153937
+ if( pCtx->iJoin && iTab>pCtx->iJoin ){
153938
+ sqlite3ErrorMsg(pWalker->pParse,
153939
+ "ON clause references tables to its right");
153940
+ return WRC_Abort;
153941
+ }
153942
+ break;
153943
+ }
153944
+ pCtx = pCtx->pParent;
153945
+ }while( pCtx );
153946
+ }
153947
+ return WRC_Continue;
153948
+}
153949
+
153950
+/*
153951
+** The xSelect callback for the search of invalid ON clause terms.
153952
+*/
153953
+static int selectCheckOnClausesSelect(Walker *pWalker, Select *pSelect){
153954
+ CheckOnCtx *pCtx = pWalker->u.pCheckOnCtx;
153955
+ if( pSelect->pSrc==pCtx->pSrc || pSelect->pSrc->nSrc==0 ){
153956
+ return WRC_Continue;
153957
+ }else{
153958
+ CheckOnCtx sCtx;
153959
+ memset(&sCtx, 0, sizeof(sCtx));
153960
+ sCtx.pSrc = pSelect->pSrc;
153961
+ sCtx.pParent = pCtx;
153962
+ pWalker->u.pCheckOnCtx = &sCtx;
153963
+ sqlite3WalkSelect(pWalker, pSelect);
153964
+ pWalker->u.pCheckOnCtx = pCtx;
153965
+ pSelect->selFlags &= ~SF_OnToWhere;
153966
+ return WRC_Prune;
153967
+ }
153968
+}
153969
+
153970
+/*
153971
+** Check all ON clauses in pSelect to verify that they do not reference
153972
+** columns to the right.
153973
+*/
153974
+static void selectCheckOnClauses(Parse *pParse, Select *pSelect){
153975
+ Walker w;
153976
+ CheckOnCtx sCtx;
153977
+ assert( pSelect->selFlags & SF_OnToWhere );
153978
+ assert( pSelect->pSrc!=0 && pSelect->pSrc->nSrc>=2 );
153979
+ memset(&w, 0, sizeof(w));
153980
+ w.pParse = pParse;
153981
+ w.xExprCallback = selectCheckOnClausesExpr;
153982
+ w.xSelectCallback = selectCheckOnClausesSelect;
153983
+ w.u.pCheckOnCtx = &sCtx;
153984
+ memset(&sCtx, 0, sizeof(sCtx));
153985
+ sCtx.pSrc = pSelect->pSrc;
153986
+ sqlite3WalkExprNN(&w, pSelect->pWhere);
153987
+ pSelect->selFlags &= ~SF_OnToWhere;
153988
+}
152779153989
152780153990
/*
152781153991
** Generate byte-code for the SELECT statement given in the p argument.
152782153992
**
152783153993
** The results are returned according to the SelectDest structure.
@@ -152901,10 +154111,22 @@
152901154111
if( sqlite3TreeTrace & 0x10 ){
152902154112
TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
152903154113
sqlite3TreeViewSelect(0, p, 0);
152904154114
}
152905154115
#endif
154116
+
154117
+ /* If the SELECT statement contains ON clauses that were moved into
154118
+ ** the WHERE clause, go through and verify that none of the terms
154119
+ ** in the ON clauses reference tables to the right of the ON clause.
154120
+ ** Do this now, after name resolution, but before query flattening
154121
+ */
154122
+ if( p->selFlags & SF_OnToWhere ){
154123
+ selectCheckOnClauses(pParse, p);
154124
+ if( pParse->nErr ){
154125
+ goto select_end;
154126
+ }
154127
+ }
152906154128
152907154129
/* If the SF_UFSrcCheck flag is set, then this function is being called
152908154130
** as part of populating the temp table for an UPDATE...FROM statement.
152909154131
** In this case, it is an error if the target object (pSrc->a[0]) name
152910154132
** or alias is duplicated within FROM clause (pSrc->a[1..n]).
@@ -153766,10 +154988,11 @@
153766154988
iBMem = pParse->nMem + 1;
153767154989
pParse->nMem += pGroupBy->nExpr;
153768154990
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
153769154991
VdbeComment((v, "clear abort flag"));
153770154992
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
154993
+ sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
153771154994
153772154995
/* Begin a loop that will extract all source rows in GROUP BY order.
153773154996
** This might involve two separate loops with an OP_Sort in between, or
153774154997
** it might be a single loop that uses an index to extract information
153775154998
** in the right order to begin with.
@@ -155466,11 +156689,14 @@
155466156689
sqlite3 *db = pParse->db;
155467156690
ExprList *pNew;
155468156691
Returning *pReturning;
155469156692
Select sSelect;
155470156693
SrcList *pFrom;
155471
- u8 fromSpace[SZ_SRCLIST_1];
156694
+ union {
156695
+ SrcList sSrc;
156696
+ u8 fromSpace[SZ_SRCLIST_1];
156697
+ } uSrc;
155472156698
155473156699
assert( v!=0 );
155474156700
if( !pParse->bReturning ){
155475156701
/* This RETURNING trigger must be for a different statement as
155476156702
** this statement lacks a RETURNING clause. */
@@ -155482,12 +156708,12 @@
155482156708
if( pTrigger != &(pReturning->retTrig) ){
155483156709
/* This RETURNING trigger is for a different statement */
155484156710
return;
155485156711
}
155486156712
memset(&sSelect, 0, sizeof(sSelect));
155487
- pFrom = (SrcList*)fromSpace;
155488
- memset(pFrom, 0, SZ_SRCLIST_1);
156713
+ memset(&uSrc, 0, sizeof(uSrc));
156714
+ pFrom = &uSrc.sSrc;
155489156715
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
155490156716
sSelect.pSrc = pFrom;
155491156717
pFrom->nSrc = 1;
155492156718
pFrom->a[0].pSTab = pTab;
155493156719
pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
@@ -163014,11 +164240,14 @@
163014164240
WhereClause *pWC = &pWInfo->sWC;
163015164241
WhereInfo *pSubWInfo;
163016164242
WhereLoop *pLoop = pLevel->pWLoop;
163017164243
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
163018164244
SrcList *pFrom;
163019
- u8 fromSpace[SZ_SRCLIST_1];
164245
+ union {
164246
+ SrcList sSrc;
164247
+ u8 fromSpace[SZ_SRCLIST_1];
164248
+ } uSrc;
163020164249
Bitmask mAll = 0;
163021164250
int k;
163022164251
163023164252
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
163024164253
sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -163058,11 +164287,11 @@
163058164287
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
163059164288
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
163060164289
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
163061164290
}
163062164291
}
163063
- pFrom = (SrcList*)fromSpace;
164292
+ pFrom = &uSrc.sSrc;
163064164293
pFrom->nSrc = 1;
163065164294
pFrom->nAlloc = 1;
163066164295
memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
163067164296
pFrom->a[0].fg.jointype = 0;
163068164297
assert( pParse->withinRJSubrtn < 100 );
@@ -164275,25 +165504,11 @@
164275165504
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
164276165505
if( ExprHasProperty(pExpr, EP_OuterON) ){
164277165506
prereqAll |= x;
164278165507
extraRight = x-1; /* ON clause terms may not be used with an index
164279165508
** on left table of a LEFT JOIN. Ticket #3015 */
164280
- if( (prereqAll>>1)>=x ){
164281
- sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
164282
- return;
164283
- }
164284165509
}else if( (prereqAll>>1)>=x ){
164285
- /* The ON clause of an INNER JOIN references a table to its right.
164286
- ** Most other SQL database engines raise an error. But SQLite versions
164287
- ** 3.0 through 3.38 just put the ON clause constraint into the WHERE
164288
- ** clause and carried on. Beginning with 3.39, raise an error only
164289
- ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite
164290
- ** more like other systems, and also preserves legacy. */
164291
- if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
164292
- sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
164293
- return;
164294
- }
164295165510
ExprClearProperty(pExpr, EP_InnerON);
164296165511
}
164297165512
}
164298165513
pTerm->prereqAll = prereqAll;
164299165514
pTerm->leftCursor = -1;
@@ -169084,10 +170299,11 @@
169084170299
if( pProbe->bNoQuery ) continue;
169085170300
rSize = pProbe->aiRowLogEst[0];
169086170301
pNew->u.btree.nEq = 0;
169087170302
pNew->u.btree.nBtm = 0;
169088170303
pNew->u.btree.nTop = 0;
170304
+ pNew->u.btree.nDistinctCol = 0;
169089170305
pNew->nSkip = 0;
169090170306
pNew->nLTerm = 0;
169091170307
pNew->iSortIdx = 0;
169092170308
pNew->rSetup = 0;
169093170309
pNew->prereq = mPrereq;
@@ -170150,14 +171366,16 @@
170150171366
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
170151171367
if( pLoop->u.vtab.isOrdered
170152171368
&& ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
170153171369
){
170154171370
obSat = obDone;
171371
+ }else{
171372
+ /* No further ORDER BY terms may be matched. So this call should
171373
+ ** return >=0, not -1. Clear isOrderDistinct to ensure it does so. */
171374
+ isOrderDistinct = 0;
170155171375
}
170156171376
break;
170157
- }else if( wctrlFlags & WHERE_DISTINCTBY ){
170158
- pLoop->u.btree.nDistinctCol = 0;
170159171377
}
170160171378
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
170161171379
170162171380
/* Mark off any ORDER BY term X that is a column in the table of
170163171381
** the current loop for which there is term in the WHERE
@@ -170897,21 +172115,28 @@
170897172115
170898172116
/* Check to see if pWLoop should be added to the set of
170899172117
** mxChoice best-so-far paths.
170900172118
**
170901172119
** First look for an existing path among best-so-far paths
170902
- ** that covers the same set of loops and has the same isOrdered
170903
- ** setting as the current path candidate.
172120
+ ** that:
172121
+ ** (1) covers the same set of loops, and
172122
+ ** (2) has a compatible isOrdered value.
172123
+ **
172124
+ ** "Compatible isOrdered value" means either
172125
+ ** (A) both have isOrdered==-1, or
172126
+ ** (B) both have isOrder>=0, or
172127
+ ** (C) ordering does not matter because this is the last round
172128
+ ** of the solver.
170904172129
**
170905172130
** The term "((pTo->isOrdered^isOrdered)&0x80)==0" is equivalent
170906172131
** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range
170907172132
** of legal values for isOrdered, -1..64.
170908172133
*/
170909172134
testcase( nTo==0 );
170910172135
for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
170911172136
if( pTo->maskLoop==maskNew
170912
- && ((pTo->isOrdered^isOrdered)&0x80)==0
172137
+ && ( ((pTo->isOrdered^isOrdered)&0x80)==0 || iLoop==nLoop-1 )
170913172138
){
170914172139
testcase( jj==nTo-1 );
170915172140
break;
170916172141
}
170917172142
}
@@ -171062,15 +172287,14 @@
171062172287
sqlite3ErrorMsg(pParse, "no query solution");
171063172288
sqlite3StackFreeNN(pParse->db, pSpace);
171064172289
return SQLITE_ERROR;
171065172290
}
171066172291
171067
- /* Find the lowest cost path. pFrom will be left pointing to that path */
172292
+ /* Only one path is available, which is the best path */
172293
+ assert( nFrom==1 );
171068172294
pFrom = aFrom;
171069
- for(ii=1; ii<nFrom; ii++){
171070
- if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii];
171071
- }
172295
+
171072172296
assert( pWInfo->nLevel==nLoop );
171073172297
/* Load the lowest cost path into pWInfo */
171074172298
for(iLoop=0; iLoop<nLoop; iLoop++){
171075172299
WhereLevel *pLevel = pWInfo->a + iLoop;
171076172300
pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
@@ -171199,11 +172423,14 @@
171199172423
int once = 0;
171200172424
#endif
171201172425
for(i=0; i<pWInfo->nLevel; i++){
171202172426
WhereLoop *p = pWInfo->a[i].pWLoop;
171203172427
if( p==0 ) break;
171204
- if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue;
172428
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
172429
+ /* Treat a vtab scan as similar to a full-table scan */
172430
+ break;
172431
+ }
171205172432
if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){
171206172433
u8 iTab = p->iTab;
171207172434
WhereLoop *pLoop;
171208172435
for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){
171209172436
if( pLoop->iTab!=iTab ) continue;
@@ -175312,11 +176539,11 @@
175312176539
** RETURN_ROW
175313176540
**
175314176541
**
175315176542
** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
175316176543
**
175317
-** ... loop started by sqlite3WhereBegin() ...
176544
+** ... loop started by sqlite3WhereBegin() ...
175318176545
** if( new partition ){
175319176546
** Gosub flush
175320176547
** }
175321176548
** Insert new row into eph table.
175322176549
** if( first row of partition ){
@@ -175830,10 +177057,16 @@
175830177057
addrStart = sqlite3VdbeCurrentAddr(v);
175831177058
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
175832177059
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
175833177060
}else{
175834177061
assert( pMWin->eEnd==TK_FOLLOWING );
177062
+ /* assert( regStart>=0 );
177063
+ ** regEnd = regEnd - regStart;
177064
+ ** regStart = 0; */
177065
+ sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd);
177066
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regStart);
177067
+
175835177068
addrStart = sqlite3VdbeCurrentAddr(v);
175836177069
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
175837177070
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
175838177071
}
175839177072
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
@@ -183317,13 +184550,10 @@
183317184550
#endif
183318184551
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
183319184552
sqlite3DbstatRegister,
183320184553
#endif
183321184554
sqlite3TestExtInit,
183322
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
183323
- sqlite3JsonTableFunctions,
183324
-#endif
183325184555
#ifdef SQLITE_ENABLE_STMTVTAB
183326184556
sqlite3StmtVtabInit,
183327184557
#endif
183328184558
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
183329184559
sqlite3VdbeBytecodeVtabInit,
@@ -184775,10 +186005,13 @@
184775186005
for(i=0; i<2 && zName==0; i++, rc &= 0xff){
184776186006
switch( rc ){
184777186007
case SQLITE_OK: zName = "SQLITE_OK"; break;
184778186008
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
184779186009
case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
186010
+ case SQLITE_ERROR_RETRY: zName = "SQLITE_ERROR_RETRY"; break;
186011
+ case SQLITE_ERROR_MISSING_COLLSEQ:
186012
+ zName = "SQLITE_ERROR_MISSING_COLLSEQ"; break;
184780186013
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
184781186014
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
184782186015
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
184783186016
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
184784186017
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
@@ -185955,10 +187188,33 @@
185955187188
}
185956187189
}
185957187190
sqlite3_mutex_leave(db->mutex);
185958187191
return z;
185959187192
}
187193
+
187194
+/*
187195
+** Set the error code and error message associated with the database handle.
187196
+**
187197
+** This routine is intended to be called by outside extensions (ex: the
187198
+** Session extension). Internal logic should invoke sqlite3Error() or
187199
+** sqlite3ErrorWithMsg() directly.
187200
+*/
187201
+SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zMsg){
187202
+ int rc = SQLITE_OK;
187203
+ if( !sqlite3SafetyCheckSickOrOk(db) ){
187204
+ return SQLITE_MISUSE_BKPT;
187205
+ }
187206
+ sqlite3_mutex_enter(db->mutex);
187207
+ if( zMsg ){
187208
+ sqlite3ErrorWithMsg(db, errcode, "%s", zMsg);
187209
+ }else{
187210
+ sqlite3Error(db, errcode);
187211
+ }
187212
+ rc = sqlite3ApiExit(db, rc);
187213
+ sqlite3_mutex_leave(db->mutex);
187214
+ return rc;
187215
+}
185960187216
185961187217
/*
185962187218
** Return the byte offset of the most recent error
185963187219
*/
185964187220
SQLITE_API int sqlite3_error_offset(sqlite3 *db){
@@ -187780,17 +189036,19 @@
187780189036
case SQLITE_TESTCTRL_ISINIT: {
187781189037
if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
187782189038
break;
187783189039
}
187784189040
187785
- /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum);
189041
+ /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, mode, tnum);
187786189042
**
187787189043
** This test control is used to create imposter tables. "db" is a pointer
187788189044
** to the database connection. dbName is the database name (ex: "main" or
187789
- ** "temp") which will receive the imposter. "onOff" turns imposter mode on
187790
- ** or off. "tnum" is the root page of the b-tree to which the imposter
187791
- ** table should connect.
189045
+ ** "temp") which will receive the imposter. "mode" turns imposter mode on
189046
+ ** or off. mode==0 means imposter mode is off. mode==1 means imposter mode
189047
+ ** is on. mode==2 means imposter mode is on but results in an imposter
189048
+ ** table that is read-only unless writable_schema is on. "tnum" is the
189049
+ ** root page of the b-tree to which the imposter table should connect.
187792189050
**
187793189051
** Enable imposter mode only when the schema has already been parsed. Then
187794189052
** run a single CREATE TABLE statement to construct the imposter table in
187795189053
** the parsed schema. Then turn imposter mode back off again.
187796189054
**
@@ -189023,21 +190281,24 @@
189023190281
**
189024190282
*/
189025190283
#ifndef _FTSINT_H
189026190284
#define _FTSINT_H
189027190285
190286
+/*
190287
+** Activate assert() only if SQLITE_TEST is enabled.
190288
+*/
190289
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
190290
+# define NDEBUG 1
190291
+#endif
190292
+
189028190293
/* #include <assert.h> */
189029190294
/* #include <stdlib.h> */
189030190295
/* #include <stddef.h> */
189031190296
/* #include <stdio.h> */
189032190297
/* #include <string.h> */
189033190298
/* #include <stdarg.h> */
189034190299
189035
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
189036
-# define NDEBUG 1
189037
-#endif
189038
-
189039190300
/* FTS3/FTS4 require virtual tables */
189040190301
#ifdef SQLITE_OMIT_VIRTUALTABLE
189041190302
# undef SQLITE_ENABLE_FTS3
189042190303
# undef SQLITE_ENABLE_FTS4
189043190304
#endif
@@ -189476,17 +190737,10 @@
189476190737
/*
189477190738
** Macro used to suppress compiler warnings for unused parameters.
189478190739
*/
189479190740
#define UNUSED_PARAMETER(x) (void)(x)
189480190741
189481
-/*
189482
-** Activate assert() only if SQLITE_TEST is enabled.
189483
-*/
189484
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
189485
-# define NDEBUG 1
189486
-#endif
189487
-
189488190742
/*
189489190743
** The TESTONLY macro is used to enclose variable declarations or
189490190744
** other bits of code that are needed to support the arguments
189491190745
** within testcase() and assert() macros.
189492190746
*/
@@ -203757,12 +205011,12 @@
203757205011
/*
203758205012
** An object of this type contains the state required to create or append
203759205013
** to an appendable b-tree segment.
203760205014
*/
203761205015
struct IncrmergeWriter {
203762
- int nLeafEst; /* Space allocated for leaf blocks */
203763
- int nWork; /* Number of leaf pages flushed */
205016
+ i64 nLeafEst; /* Space allocated for leaf blocks */
205017
+ i64 nWork; /* Number of leaf pages flushed */
203764205018
sqlite3_int64 iAbsLevel; /* Absolute level of input segments */
203765205019
int iIdx; /* Index of *output* segment in iAbsLevel+1 */
203766205020
sqlite3_int64 iStart; /* Block number of first allocated block */
203767205021
sqlite3_int64 iEnd; /* Block number of last allocated block */
203768205022
sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
@@ -204504,21 +205758,21 @@
204504205758
Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */
204505205759
IncrmergeWriter *pWriter /* Populate this object */
204506205760
){
204507205761
int rc; /* Return Code */
204508205762
int i; /* Iterator variable */
204509
- int nLeafEst = 0; /* Blocks allocated for leaf nodes */
205763
+ i64 nLeafEst = 0; /* Blocks allocated for leaf nodes */
204510205764
sqlite3_stmt *pLeafEst = 0; /* SQL used to determine nLeafEst */
204511205765
sqlite3_stmt *pFirstBlock = 0; /* SQL used to determine first block */
204512205766
204513205767
/* Calculate nLeafEst. */
204514205768
rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0);
204515205769
if( rc==SQLITE_OK ){
204516205770
sqlite3_bind_int64(pLeafEst, 1, iAbsLevel);
204517205771
sqlite3_bind_int64(pLeafEst, 2, pCsr->nSegment);
204518205772
if( SQLITE_ROW==sqlite3_step(pLeafEst) ){
204519
- nLeafEst = sqlite3_column_int(pLeafEst, 0);
205773
+ nLeafEst = sqlite3_column_int64(pLeafEst, 0);
204520205774
}
204521205775
rc = sqlite3_reset(pLeafEst);
204522205776
}
204523205777
if( rc!=SQLITE_OK ) return rc;
204524205778
@@ -205897,14 +207151,10 @@
205897207151
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
205898207152
205899207153
/* #include <string.h> */
205900207154
/* #include <assert.h> */
205901207155
205902
-#ifndef SQLITE_AMALGAMATION
205903
-typedef sqlite3_int64 i64;
205904
-#endif
205905
-
205906207156
/*
205907207157
** Characters that may appear in the second argument to matchinfo().
205908207158
*/
205909207159
#define FTS3_MATCHINFO_NPHRASE 'p' /* 1 value */
205910207160
#define FTS3_MATCHINFO_NCOL 'c' /* 1 value */
@@ -210754,11 +212004,11 @@
210754212004
switch( (u8)zIn[1] ){
210755212005
case '\'':
210756212006
jsonAppendChar(pOut, '\'');
210757212007
break;
210758212008
case 'v':
210759
- jsonAppendRawNZ(pOut, "\\u0009", 6);
212009
+ jsonAppendRawNZ(pOut, "\\u000b", 6);
210760212010
break;
210761212011
case 'x':
210762212012
if( sz2<4 ){
210763212013
pOut->eErr |= JSTRING_MALFORMED;
210764212014
sz2 = 2;
@@ -211604,23 +212854,31 @@
211604212854
/*
211605212855
** Return the value of the BLOB node at index i.
211606212856
**
211607212857
** If the value is a primitive, return it as an SQL value.
211608212858
** If the value is an array or object, return it as either
211609
-** JSON text or the BLOB encoding, depending on the JSON_B flag
211610
-** on the userdata.
212859
+** JSON text or the BLOB encoding, depending on the eMode flag
212860
+** as follows:
212861
+**
212862
+** eMode==0 JSONB if the JSON_B flag is set in userdata or
212863
+** text if the JSON_B flag is omitted from userdata.
212864
+**
212865
+** eMode==1 Text
212866
+**
212867
+** eMode==2 JSONB
211611212868
*/
211612212869
static void jsonReturnFromBlob(
211613212870
JsonParse *pParse, /* Complete JSON parse tree */
211614212871
u32 i, /* Index of the node */
211615212872
sqlite3_context *pCtx, /* Return value for this function */
211616
- int textOnly /* return text JSON. Disregard user-data */
212873
+ int eMode /* Format of return: text of JSONB */
211617212874
){
211618212875
u32 n, sz;
211619212876
int rc;
211620212877
sqlite3 *db = sqlite3_context_db_handle(pCtx);
211621212878
212879
+ assert( eMode>=0 && eMode<=2 );
211622212880
n = jsonbPayloadSize(pParse, i, &sz);
211623212881
if( n==0 ){
211624212882
sqlite3_result_error(pCtx, "malformed JSON", -1);
211625212883
return;
211626212884
}
@@ -211657,11 +212915,23 @@
211657212915
z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
211658212916
if( z==0 ) goto returnfromblob_oom;
211659212917
rc = sqlite3DecOrHexToI64(z, &iRes);
211660212918
sqlite3DbFree(db, z);
211661212919
if( rc==0 ){
211662
- sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
212920
+ if( iRes<0 ){
212921
+ /* A hexadecimal literal with 16 significant digits and with the
212922
+ ** high-order bit set is a negative integer in SQLite (and hence
212923
+ ** iRes comes back as negative) but should be interpreted as a
212924
+ ** positive value if it occurs within JSON. The value is too
212925
+ ** large to appear as an SQLite integer so it must be converted
212926
+ ** into floating point. */
212927
+ double r;
212928
+ r = (double)*(sqlite3_uint64*)&iRes;
212929
+ sqlite3_result_double(pCtx, bNeg ? -r : r);
212930
+ }else{
212931
+ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
212932
+ }
211663212933
}else if( rc==3 && bNeg ){
211664212934
sqlite3_result_int64(pCtx, SMALLEST_INT64);
211665212935
}else if( rc==1 ){
211666212936
goto returnfromblob_malformed;
211667212937
}else{
@@ -211735,12 +213005,18 @@
211735213005
sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
211736213006
break;
211737213007
}
211738213008
case JSONB_ARRAY:
211739213009
case JSONB_OBJECT: {
211740
- int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
211741
- if( flags & JSON_BLOB ){
213010
+ if( eMode==0 ){
213011
+ if( (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)) & JSON_BLOB)!=0 ){
213012
+ eMode = 2;
213013
+ }else{
213014
+ eMode = 1;
213015
+ }
213016
+ }
213017
+ if( eMode==2 ){
211742213018
sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
211743213019
}else{
211744213020
jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
211745213021
}
211746213022
break;
@@ -213383,10 +214659,11 @@
213383214659
u32 i; /* Index in sParse.aBlob[] of current row */
213384214660
u32 iEnd; /* EOF when i equals or exceeds this value */
213385214661
u32 nRoot; /* Size of the root path in bytes */
213386214662
u8 eType; /* Type of the container for element i */
213387214663
u8 bRecursive; /* True for json_tree(). False for json_each() */
214664
+ u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */
213388214665
u32 nParent; /* Current nesting depth */
213389214666
u32 nParentAlloc; /* Space allocated for aParent[] */
213390214667
JsonParent *aParent; /* Parent elements of i */
213391214668
sqlite3 *db; /* Database connection */
213392214669
JsonString path; /* Current path */
@@ -213394,10 +214671,12 @@
213394214671
};
213395214672
typedef struct JsonEachConnection JsonEachConnection;
213396214673
struct JsonEachConnection {
213397214674
sqlite3_vtab base; /* Base class - must be first */
213398214675
sqlite3 *db; /* Database connection */
214676
+ u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */
214677
+ u8 bRecursive; /* True for json_tree(). False for json_each() */
213399214678
};
213400214679
213401214680
213402214681
/* Constructor for the json_each virtual table */
213403214682
static int jsonEachConnect(
@@ -213436,10 +214715,12 @@
213436214715
pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
213437214716
*ppVtab = (sqlite3_vtab*)pNew;
213438214717
if( pNew==0 ) return SQLITE_NOMEM;
213439214718
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
213440214719
pNew->db = db;
214720
+ pNew->eMode = argv[0][4]=='b' ? 2 : 1;
214721
+ pNew->bRecursive = argv[0][4+pNew->eMode]=='t';
213441214722
}
213442214723
return rc;
213443214724
}
213444214725
213445214726
/* destructor for json_each virtual table */
@@ -213447,34 +214728,26 @@
213447214728
JsonEachConnection *p = (JsonEachConnection*)pVtab;
213448214729
sqlite3DbFree(p->db, pVtab);
213449214730
return SQLITE_OK;
213450214731
}
213451214732
213452
-/* constructor for a JsonEachCursor object for json_each(). */
213453
-static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
214733
+/* constructor for a JsonEachCursor object for json_each()/json_tree(). */
214734
+static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
213454214735
JsonEachConnection *pVtab = (JsonEachConnection*)p;
213455214736
JsonEachCursor *pCur;
213456214737
213457214738
UNUSED_PARAMETER(p);
213458214739
pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
213459214740
if( pCur==0 ) return SQLITE_NOMEM;
213460214741
pCur->db = pVtab->db;
214742
+ pCur->eMode = pVtab->eMode;
214743
+ pCur->bRecursive = pVtab->bRecursive;
213461214744
jsonStringZero(&pCur->path);
213462214745
*ppCursor = &pCur->base;
213463214746
return SQLITE_OK;
213464214747
}
213465214748
213466
-/* constructor for a JsonEachCursor object for json_tree(). */
213467
-static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
213468
- int rc = jsonEachOpenEach(p, ppCursor);
213469
- if( rc==SQLITE_OK ){
213470
- JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
213471
- pCur->bRecursive = 1;
213472
- }
213473
- return rc;
213474
-}
213475
-
213476214749
/* Reset a JsonEachCursor back to its original state. Free any memory
213477214750
** held. */
213478214751
static void jsonEachCursorReset(JsonEachCursor *p){
213479214752
jsonParseReset(&p->sParse);
213480214753
jsonStringReset(&p->path);
@@ -213675,11 +214948,11 @@
213675214948
}
213676214949
break;
213677214950
}
213678214951
case JEACH_VALUE: {
213679214952
u32 i = jsonSkipLabel(p);
213680
- jsonReturnFromBlob(&p->sParse, i, ctx, 1);
214953
+ jsonReturnFromBlob(&p->sParse, i, ctx, p->eMode);
213681214954
if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
213682214955
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
213683214956
}
213684214957
break;
213685214958
}
@@ -213919,40 +215192,11 @@
213919215192
0, /* xCreate */
213920215193
jsonEachConnect, /* xConnect */
213921215194
jsonEachBestIndex, /* xBestIndex */
213922215195
jsonEachDisconnect, /* xDisconnect */
213923215196
0, /* xDestroy */
213924
- jsonEachOpenEach, /* xOpen - open a cursor */
213925
- jsonEachClose, /* xClose - close a cursor */
213926
- jsonEachFilter, /* xFilter - configure scan constraints */
213927
- jsonEachNext, /* xNext - advance a cursor */
213928
- jsonEachEof, /* xEof - check for end of scan */
213929
- jsonEachColumn, /* xColumn - read data */
213930
- jsonEachRowid, /* xRowid - read data */
213931
- 0, /* xUpdate */
213932
- 0, /* xBegin */
213933
- 0, /* xSync */
213934
- 0, /* xCommit */
213935
- 0, /* xRollback */
213936
- 0, /* xFindMethod */
213937
- 0, /* xRename */
213938
- 0, /* xSavepoint */
213939
- 0, /* xRelease */
213940
- 0, /* xRollbackTo */
213941
- 0, /* xShadowName */
213942
- 0 /* xIntegrity */
213943
-};
213944
-
213945
-/* The methods of the json_tree virtual table. */
213946
-static sqlite3_module jsonTreeModule = {
213947
- 0, /* iVersion */
213948
- 0, /* xCreate */
213949
- jsonEachConnect, /* xConnect */
213950
- jsonEachBestIndex, /* xBestIndex */
213951
- jsonEachDisconnect, /* xDisconnect */
213952
- 0, /* xDestroy */
213953
- jsonEachOpenTree, /* xOpen - open a cursor */
215197
+ jsonEachOpen, /* xOpen - open a cursor */
213954215198
jsonEachClose, /* xClose - close a cursor */
213955215199
jsonEachFilter, /* xFilter - configure scan constraints */
213956215200
jsonEachNext, /* xNext - advance a cursor */
213957215201
jsonEachEof, /* xEof - check for end of scan */
213958215202
jsonEachColumn, /* xColumn - read data */
@@ -214037,26 +215281,25 @@
214037215281
#endif
214038215282
}
214039215283
214040215284
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
214041215285
/*
214042
-** Register the JSON table-valued functions
215286
+** Register the JSON table-valued function named zName and return a
215287
+** pointer to its Module object. Return NULL if something goes wrong.
214043215288
*/
214044
-SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3 *db){
214045
- int rc = SQLITE_OK;
214046
- static const struct {
214047
- const char *zName;
214048
- sqlite3_module *pModule;
214049
- } aMod[] = {
214050
- { "json_each", &jsonEachModule },
214051
- { "json_tree", &jsonTreeModule },
214052
- };
215289
+SQLITE_PRIVATE Module *sqlite3JsonVtabRegister(sqlite3 *db, const char *zName){
214053215290
unsigned int i;
214054
- for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
214055
- rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
215291
+ static const char *azModule[] = {
215292
+ "json_each", "json_tree", "jsonb_each", "jsonb_tree"
215293
+ };
215294
+ assert( sqlite3HashFind(&db->aModule, zName)==0 );
215295
+ for(i=0; i<sizeof(azModule)/sizeof(azModule[0]); i++){
215296
+ if( sqlite3StrICmp(azModule[i],zName)==0 ){
215297
+ return sqlite3VtabCreateModule(db, azModule[i], &jsonEachModule, 0, 0);
215298
+ }
214056215299
}
214057
- return rc;
215300
+ return 0;
214058215301
}
214059215302
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) */
214060215303
214061215304
/************** End of json.c ************************************************/
214062215305
/************** Begin file rtree.c *******************************************/
@@ -228289,12 +229532,12 @@
228289229532
typedef struct DbpageTable DbpageTable;
228290229533
typedef struct DbpageCursor DbpageCursor;
228291229534
228292229535
struct DbpageCursor {
228293229536
sqlite3_vtab_cursor base; /* Base class. Must be first */
228294
- int pgno; /* Current page number */
228295
- int mxPgno; /* Last page to visit on this scan */
229537
+ Pgno pgno; /* Current page number */
229538
+ Pgno mxPgno; /* Last page to visit on this scan */
228296229539
Pager *pPager; /* Pager being read/written */
228297229540
DbPage *pPage1; /* Page 1 of the database */
228298229541
int iDb; /* Index of database to analyze */
228299229542
int szPage; /* Size of each page in bytes */
228300229543
};
@@ -228427,11 +229670,11 @@
228427229670
if( pCsr==0 ){
228428229671
return SQLITE_NOMEM_BKPT;
228429229672
}else{
228430229673
memset(pCsr, 0, sizeof(DbpageCursor));
228431229674
pCsr->base.pVtab = pVTab;
228432
- pCsr->pgno = -1;
229675
+ pCsr->pgno = 0;
228433229676
}
228434229677
228435229678
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
228436229679
return SQLITE_OK;
228437229680
}
@@ -228527,16 +229770,16 @@
228527229770
){
228528229771
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
228529229772
int rc = SQLITE_OK;
228530229773
switch( i ){
228531229774
case 0: { /* pgno */
228532
- sqlite3_result_int(ctx, pCsr->pgno);
229775
+ sqlite3_result_int64(ctx, (sqlite3_int64)pCsr->pgno);
228533229776
break;
228534229777
}
228535229778
case 1: { /* data */
228536229779
DbPage *pDbPage = 0;
228537
- if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
229780
+ if( pCsr->pgno==(Pgno)((PENDING_BYTE/pCsr->szPage)+1) ){
228538229781
/* The pending byte page. Assume it is zeroed out. Attempting to
228539229782
** request this page from the page is an SQLITE_CORRUPT error. */
228540229783
sqlite3_result_zeroblob(ctx, pCsr->szPage);
228541229784
}else{
228542229785
rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
@@ -228606,14 +229849,14 @@
228606229849
if( argc==1 ){
228607229850
zErr = "cannot delete";
228608229851
goto update_fail;
228609229852
}
228610229853
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
228611
- pgno = (Pgno)sqlite3_value_int(argv[2]);
229854
+ pgno = (Pgno)sqlite3_value_int64(argv[2]);
228612229855
isInsert = 1;
228613229856
}else{
228614
- pgno = sqlite3_value_int(argv[0]);
229857
+ pgno = (Pgno)sqlite3_value_int64(argv[0]);
228615229858
if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
228616229859
zErr = "cannot insert";
228617229860
goto update_fail;
228618229861
}
228619229862
isInsert = 0;
@@ -228744,10 +229987,539 @@
228744229987
#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
228745229988
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
228746229989
#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
228747229990
228748229991
/************** End of dbpage.c **********************************************/
229992
+/************** Begin file carray.c ******************************************/
229993
+/*
229994
+** 2016-06-29
229995
+**
229996
+** The author disclaims copyright to this source code. In place of
229997
+** a legal notice, here is a blessing:
229998
+**
229999
+** May you do good and not evil.
230000
+** May you find forgiveness for yourself and forgive others.
230001
+** May you share freely, never taking more than you give.
230002
+**
230003
+*************************************************************************
230004
+**
230005
+** This file implements a table-valued-function that
230006
+** returns the values in a C-language array.
230007
+** Examples:
230008
+**
230009
+** SELECT * FROM carray($ptr,5)
230010
+**
230011
+** The query above returns 5 integers contained in a C-language array
230012
+** at the address $ptr. $ptr is a pointer to the array of integers.
230013
+** The pointer value must be assigned to $ptr using the
230014
+** sqlite3_bind_pointer() interface with a pointer type of "carray".
230015
+** For example:
230016
+**
230017
+** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
230018
+** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
230019
+** sqlite3_bind_pointer(pStmt, i, aX, "carray", 0);
230020
+**
230021
+** There is an optional third parameter to determine the datatype of
230022
+** the C-language array. Allowed values of the third parameter are
230023
+** 'int32', 'int64', 'double', 'char*', 'struct iovec'. Example:
230024
+**
230025
+** SELECT * FROM carray($ptr,10,'char*');
230026
+**
230027
+** The default value of the third parameter is 'int32'.
230028
+**
230029
+** HOW IT WORKS
230030
+**
230031
+** The carray "function" is really a virtual table with the
230032
+** following schema:
230033
+**
230034
+** CREATE TABLE carray(
230035
+** value,
230036
+** pointer HIDDEN,
230037
+** count HIDDEN,
230038
+** ctype TEXT HIDDEN
230039
+** );
230040
+**
230041
+** If the hidden columns "pointer" and "count" are unconstrained, then
230042
+** the virtual table has no rows. Otherwise, the virtual table interprets
230043
+** the integer value of "pointer" as a pointer to the array and "count"
230044
+** as the number of elements in the array. The virtual table steps through
230045
+** the array, element by element.
230046
+*/
230047
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY)
230048
+/* #include "sqliteInt.h" */
230049
+#if defined(_WIN32) || defined(__RTP__) || defined(_WRS_KERNEL)
230050
+ struct iovec {
230051
+ void *iov_base;
230052
+ size_t iov_len;
230053
+ };
230054
+#else
230055
+# include <sys/uio.h>
230056
+#endif
230057
+
230058
+/*
230059
+** Names of allowed datatypes
230060
+*/
230061
+static const char *azType[] = { "int32", "int64", "double", "char*",
230062
+ "struct iovec" };
230063
+
230064
+/*
230065
+** Structure used to hold the sqlite3_carray_bind() information
230066
+*/
230067
+typedef struct carray_bind carray_bind;
230068
+struct carray_bind {
230069
+ void *aData; /* The data */
230070
+ int nData; /* Number of elements */
230071
+ int mFlags; /* Control flags */
230072
+ void (*xDel)(void*); /* Destructor for aData */
230073
+};
230074
+
230075
+
230076
+/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
230077
+** serve as the underlying representation of a cursor that scans
230078
+** over rows of the result
230079
+*/
230080
+typedef struct carray_cursor carray_cursor;
230081
+struct carray_cursor {
230082
+ sqlite3_vtab_cursor base; /* Base class - must be first */
230083
+ sqlite3_int64 iRowid; /* The rowid */
230084
+ void *pPtr; /* Pointer to the array of values */
230085
+ sqlite3_int64 iCnt; /* Number of integers in the array */
230086
+ unsigned char eType; /* One of the CARRAY_type values */
230087
+};
230088
+
230089
+/*
230090
+** The carrayConnect() method is invoked to create a new
230091
+** carray_vtab that describes the carray virtual table.
230092
+**
230093
+** Think of this routine as the constructor for carray_vtab objects.
230094
+**
230095
+** All this routine needs to do is:
230096
+**
230097
+** (1) Allocate the carray_vtab object and initialize all fields.
230098
+**
230099
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
230100
+** result set of queries against carray will look like.
230101
+*/
230102
+static int carrayConnect(
230103
+ sqlite3 *db,
230104
+ void *pAux,
230105
+ int argc, const char *const*argv,
230106
+ sqlite3_vtab **ppVtab,
230107
+ char **pzErr
230108
+){
230109
+ sqlite3_vtab *pNew;
230110
+ int rc;
230111
+
230112
+/* Column numbers */
230113
+#define CARRAY_COLUMN_VALUE 0
230114
+#define CARRAY_COLUMN_POINTER 1
230115
+#define CARRAY_COLUMN_COUNT 2
230116
+#define CARRAY_COLUMN_CTYPE 3
230117
+
230118
+ rc = sqlite3_declare_vtab(db,
230119
+ "CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
230120
+ if( rc==SQLITE_OK ){
230121
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
230122
+ if( pNew==0 ) return SQLITE_NOMEM;
230123
+ memset(pNew, 0, sizeof(*pNew));
230124
+ }
230125
+ return rc;
230126
+}
230127
+
230128
+/*
230129
+** This method is the destructor for carray_cursor objects.
230130
+*/
230131
+static int carrayDisconnect(sqlite3_vtab *pVtab){
230132
+ sqlite3_free(pVtab);
230133
+ return SQLITE_OK;
230134
+}
230135
+
230136
+/*
230137
+** Constructor for a new carray_cursor object.
230138
+*/
230139
+static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
230140
+ carray_cursor *pCur;
230141
+ pCur = sqlite3_malloc( sizeof(*pCur) );
230142
+ if( pCur==0 ) return SQLITE_NOMEM;
230143
+ memset(pCur, 0, sizeof(*pCur));
230144
+ *ppCursor = &pCur->base;
230145
+ return SQLITE_OK;
230146
+}
230147
+
230148
+/*
230149
+** Destructor for a carray_cursor.
230150
+*/
230151
+static int carrayClose(sqlite3_vtab_cursor *cur){
230152
+ sqlite3_free(cur);
230153
+ return SQLITE_OK;
230154
+}
230155
+
230156
+
230157
+/*
230158
+** Advance a carray_cursor to its next row of output.
230159
+*/
230160
+static int carrayNext(sqlite3_vtab_cursor *cur){
230161
+ carray_cursor *pCur = (carray_cursor*)cur;
230162
+ pCur->iRowid++;
230163
+ return SQLITE_OK;
230164
+}
230165
+
230166
+/*
230167
+** Return values of columns for the row at which the carray_cursor
230168
+** is currently pointing.
230169
+*/
230170
+static int carrayColumn(
230171
+ sqlite3_vtab_cursor *cur, /* The cursor */
230172
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
230173
+ int i /* Which column to return */
230174
+){
230175
+ carray_cursor *pCur = (carray_cursor*)cur;
230176
+ sqlite3_int64 x = 0;
230177
+ switch( i ){
230178
+ case CARRAY_COLUMN_POINTER: return SQLITE_OK;
230179
+ case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
230180
+ case CARRAY_COLUMN_CTYPE: {
230181
+ sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
230182
+ return SQLITE_OK;
230183
+ }
230184
+ default: {
230185
+ switch( pCur->eType ){
230186
+ case CARRAY_INT32: {
230187
+ int *p = (int*)pCur->pPtr;
230188
+ sqlite3_result_int(ctx, p[pCur->iRowid-1]);
230189
+ return SQLITE_OK;
230190
+ }
230191
+ case CARRAY_INT64: {
230192
+ sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr;
230193
+ sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
230194
+ return SQLITE_OK;
230195
+ }
230196
+ case CARRAY_DOUBLE: {
230197
+ double *p = (double*)pCur->pPtr;
230198
+ sqlite3_result_double(ctx, p[pCur->iRowid-1]);
230199
+ return SQLITE_OK;
230200
+ }
230201
+ case CARRAY_TEXT: {
230202
+ const char **p = (const char**)pCur->pPtr;
230203
+ sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
230204
+ return SQLITE_OK;
230205
+ }
230206
+ default: {
230207
+ const struct iovec *p = (struct iovec*)pCur->pPtr;
230208
+ assert( pCur->eType==CARRAY_BLOB );
230209
+ sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base,
230210
+ (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT);
230211
+ return SQLITE_OK;
230212
+ }
230213
+ }
230214
+ }
230215
+ }
230216
+ sqlite3_result_int64(ctx, x);
230217
+ return SQLITE_OK;
230218
+}
230219
+
230220
+/*
230221
+** Return the rowid for the current row. In this implementation, the
230222
+** rowid is the same as the output value.
230223
+*/
230224
+static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
230225
+ carray_cursor *pCur = (carray_cursor*)cur;
230226
+ *pRowid = pCur->iRowid;
230227
+ return SQLITE_OK;
230228
+}
230229
+
230230
+/*
230231
+** Return TRUE if the cursor has been moved off of the last
230232
+** row of output.
230233
+*/
230234
+static int carrayEof(sqlite3_vtab_cursor *cur){
230235
+ carray_cursor *pCur = (carray_cursor*)cur;
230236
+ return pCur->iRowid>pCur->iCnt;
230237
+}
230238
+
230239
+/*
230240
+** This method is called to "rewind" the carray_cursor object back
230241
+** to the first row of output.
230242
+*/
230243
+static int carrayFilter(
230244
+ sqlite3_vtab_cursor *pVtabCursor,
230245
+ int idxNum, const char *idxStr,
230246
+ int argc, sqlite3_value **argv
230247
+){
230248
+ carray_cursor *pCur = (carray_cursor *)pVtabCursor;
230249
+ pCur->pPtr = 0;
230250
+ pCur->iCnt = 0;
230251
+ switch( idxNum ){
230252
+ case 1: {
230253
+ carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
230254
+ if( pBind==0 ) break;
230255
+ pCur->pPtr = pBind->aData;
230256
+ pCur->iCnt = pBind->nData;
230257
+ pCur->eType = pBind->mFlags & 0x07;
230258
+ break;
230259
+ }
230260
+ case 2:
230261
+ case 3: {
230262
+ pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
230263
+ pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
230264
+ if( idxNum<3 ){
230265
+ pCur->eType = CARRAY_INT32;
230266
+ }else{
230267
+ unsigned char i;
230268
+ const char *zType = (const char*)sqlite3_value_text(argv[2]);
230269
+ for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
230270
+ if( sqlite3_stricmp(zType, azType[i])==0 ) break;
230271
+ }
230272
+ if( i>=sizeof(azType)/sizeof(azType[0]) ){
230273
+ pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
230274
+ "unknown datatype: %Q", zType);
230275
+ return SQLITE_ERROR;
230276
+ }else{
230277
+ pCur->eType = i;
230278
+ }
230279
+ }
230280
+ break;
230281
+ }
230282
+ }
230283
+ pCur->iRowid = 1;
230284
+ return SQLITE_OK;
230285
+}
230286
+
230287
+/*
230288
+** SQLite will invoke this method one or more times while planning a query
230289
+** that uses the carray virtual table. This routine needs to create
230290
+** a query plan for each invocation and compute an estimated cost for that
230291
+** plan.
230292
+**
230293
+** In this implementation idxNum is used to represent the
230294
+** query plan. idxStr is unused.
230295
+**
230296
+** idxNum is:
230297
+**
230298
+** 1 If only the pointer= constraint exists. In this case, the
230299
+** parameter must be bound using sqlite3_carray_bind().
230300
+**
230301
+** 2 if the pointer= and count= constraints exist.
230302
+**
230303
+** 3 if the ctype= constraint also exists.
230304
+**
230305
+** idxNum is 0 otherwise and carray becomes an empty table.
230306
+*/
230307
+static int carrayBestIndex(
230308
+ sqlite3_vtab *tab,
230309
+ sqlite3_index_info *pIdxInfo
230310
+){
230311
+ int i; /* Loop over constraints */
230312
+ int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
230313
+ int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
230314
+ int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
230315
+ unsigned seen = 0; /* Bitmask of == constrainted columns */
230316
+
230317
+ const struct sqlite3_index_constraint *pConstraint;
230318
+ pConstraint = pIdxInfo->aConstraint;
230319
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
230320
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
230321
+ if( pConstraint->iColumn>=0 ) seen |= 1 << pConstraint->iColumn;
230322
+ if( pConstraint->usable==0 ) continue;
230323
+ switch( pConstraint->iColumn ){
230324
+ case CARRAY_COLUMN_POINTER:
230325
+ ptrIdx = i;
230326
+ break;
230327
+ case CARRAY_COLUMN_COUNT:
230328
+ cntIdx = i;
230329
+ break;
230330
+ case CARRAY_COLUMN_CTYPE:
230331
+ ctypeIdx = i;
230332
+ break;
230333
+ }
230334
+ }
230335
+ if( ptrIdx>=0 ){
230336
+ pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
230337
+ pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
230338
+ pIdxInfo->estimatedCost = (double)1;
230339
+ pIdxInfo->estimatedRows = 100;
230340
+ pIdxInfo->idxNum = 1;
230341
+ if( cntIdx>=0 ){
230342
+ pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
230343
+ pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
230344
+ pIdxInfo->idxNum = 2;
230345
+ if( ctypeIdx>=0 ){
230346
+ pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
230347
+ pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
230348
+ pIdxInfo->idxNum = 3;
230349
+ }else if( seen & (1<<CARRAY_COLUMN_CTYPE) ){
230350
+ /* In a three-argument carray(), we need to know the value of all
230351
+ ** three arguments */
230352
+ return SQLITE_CONSTRAINT;
230353
+ }
230354
+ }else if( seen & (1<<CARRAY_COLUMN_COUNT) ){
230355
+ /* In a two-argument carray(), we need to know the value of both
230356
+ ** arguments */
230357
+ return SQLITE_CONSTRAINT;
230358
+ }
230359
+ }else{
230360
+ pIdxInfo->estimatedCost = (double)2147483647;
230361
+ pIdxInfo->estimatedRows = 2147483647;
230362
+ pIdxInfo->idxNum = 0;
230363
+ }
230364
+ return SQLITE_OK;
230365
+}
230366
+
230367
+/*
230368
+** This following structure defines all the methods for the
230369
+** carray virtual table.
230370
+*/
230371
+static sqlite3_module carrayModule = {
230372
+ 0, /* iVersion */
230373
+ 0, /* xCreate */
230374
+ carrayConnect, /* xConnect */
230375
+ carrayBestIndex, /* xBestIndex */
230376
+ carrayDisconnect, /* xDisconnect */
230377
+ 0, /* xDestroy */
230378
+ carrayOpen, /* xOpen - open a cursor */
230379
+ carrayClose, /* xClose - close a cursor */
230380
+ carrayFilter, /* xFilter - configure scan constraints */
230381
+ carrayNext, /* xNext - advance a cursor */
230382
+ carrayEof, /* xEof - check for end of scan */
230383
+ carrayColumn, /* xColumn - read data */
230384
+ carrayRowid, /* xRowid - read data */
230385
+ 0, /* xUpdate */
230386
+ 0, /* xBegin */
230387
+ 0, /* xSync */
230388
+ 0, /* xCommit */
230389
+ 0, /* xRollback */
230390
+ 0, /* xFindMethod */
230391
+ 0, /* xRename */
230392
+ 0, /* xSavepoint */
230393
+ 0, /* xRelease */
230394
+ 0, /* xRollbackTo */
230395
+ 0, /* xShadow */
230396
+ 0 /* xIntegrity */
230397
+};
230398
+
230399
+/*
230400
+** Destructor for the carray_bind object
230401
+*/
230402
+static void carrayBindDel(void *pPtr){
230403
+ carray_bind *p = (carray_bind*)pPtr;
230404
+ if( p->xDel!=SQLITE_STATIC ){
230405
+ p->xDel(p->aData);
230406
+ }
230407
+ sqlite3_free(p);
230408
+}
230409
+
230410
+/*
230411
+** Invoke this interface in order to bind to the single-argument
230412
+** version of CARRAY().
230413
+*/
230414
+SQLITE_API int sqlite3_carray_bind(
230415
+ sqlite3_stmt *pStmt,
230416
+ int idx,
230417
+ void *aData,
230418
+ int nData,
230419
+ int mFlags,
230420
+ void (*xDestroy)(void*)
230421
+){
230422
+ carray_bind *pNew = 0;
230423
+ int i;
230424
+ int rc = SQLITE_OK;
230425
+
230426
+ /* Ensure that the mFlags value is acceptable. */
230427
+ assert( CARRAY_INT32==0 && CARRAY_INT64==1 && CARRAY_DOUBLE==2 );
230428
+ assert( CARRAY_TEXT==3 && CARRAY_BLOB==4 );
230429
+ if( mFlags<CARRAY_INT32 || mFlags>CARRAY_BLOB ){
230430
+ rc = SQLITE_ERROR;
230431
+ goto carray_bind_error;
230432
+ }
230433
+
230434
+ pNew = sqlite3_malloc64(sizeof(*pNew));
230435
+ if( pNew==0 ){
230436
+ rc = SQLITE_NOMEM;
230437
+ goto carray_bind_error;
230438
+ }
230439
+
230440
+ pNew->nData = nData;
230441
+ pNew->mFlags = mFlags;
230442
+ if( xDestroy==SQLITE_TRANSIENT ){
230443
+ sqlite3_int64 sz = nData;
230444
+ switch( mFlags ){
230445
+ case CARRAY_INT32: sz *= 4; break;
230446
+ case CARRAY_INT64: sz *= 8; break;
230447
+ case CARRAY_DOUBLE: sz *= 8; break;
230448
+ case CARRAY_TEXT: sz *= sizeof(char*); break;
230449
+ default: sz *= sizeof(struct iovec); break;
230450
+ }
230451
+ if( mFlags==CARRAY_TEXT ){
230452
+ for(i=0; i<nData; i++){
230453
+ const char *z = ((char**)aData)[i];
230454
+ if( z ) sz += strlen(z) + 1;
230455
+ }
230456
+ }else if( mFlags==CARRAY_BLOB ){
230457
+ for(i=0; i<nData; i++){
230458
+ sz += ((struct iovec*)aData)[i].iov_len;
230459
+ }
230460
+ }
230461
+
230462
+ pNew->aData = sqlite3_malloc64( sz );
230463
+ if( pNew->aData==0 ){
230464
+ rc = SQLITE_NOMEM;
230465
+ goto carray_bind_error;
230466
+ }
230467
+
230468
+ if( mFlags==CARRAY_TEXT ){
230469
+ char **az = (char**)pNew->aData;
230470
+ char *z = (char*)&az[nData];
230471
+ for(i=0; i<nData; i++){
230472
+ const char *zData = ((char**)aData)[i];
230473
+ sqlite3_int64 n;
230474
+ if( zData==0 ){
230475
+ az[i] = 0;
230476
+ continue;
230477
+ }
230478
+ az[i] = z;
230479
+ n = strlen(zData);
230480
+ memcpy(z, zData, n+1);
230481
+ z += n+1;
230482
+ }
230483
+ }else if( mFlags==CARRAY_BLOB ){
230484
+ struct iovec *p = (struct iovec*)pNew->aData;
230485
+ unsigned char *z = (unsigned char*)&p[nData];
230486
+ for(i=0; i<nData; i++){
230487
+ size_t n = ((struct iovec*)aData)[i].iov_len;
230488
+ p[i].iov_len = n;
230489
+ p[i].iov_base = z;
230490
+ z += n;
230491
+ memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n);
230492
+ }
230493
+ }else{
230494
+ memcpy(pNew->aData, aData, sz);
230495
+ }
230496
+ pNew->xDel = sqlite3_free;
230497
+ }else{
230498
+ pNew->aData = aData;
230499
+ pNew->xDel = xDestroy;
230500
+ }
230501
+ return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
230502
+
230503
+ carray_bind_error:
230504
+ if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
230505
+ xDestroy(aData);
230506
+ }
230507
+ sqlite3_free(pNew);
230508
+ return rc;
230509
+}
230510
+
230511
+/*
230512
+** Invoke this routine to register the carray() function.
230513
+*/
230514
+SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3 *db){
230515
+ return sqlite3VtabCreateModule(db, "carray", &carrayModule, 0, 0);
230516
+}
230517
+
230518
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY) */
230519
+
230520
+/************** End of carray.c **********************************************/
228749230521
/************** Begin file sqlite3session.c **********************************/
228750230522
228751230523
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
228752230524
/* #include "sqlite3session.h" */
228753230525
/* #include <assert.h> */
@@ -231561,10 +233333,23 @@
231561233333
assert( (a - p->aRecord)==p->nRecord );
231562233334
}
231563233335
231564233336
return rc;
231565233337
}
233338
+
233339
+static int sessionPrepare(
233340
+ sqlite3 *db,
233341
+ sqlite3_stmt **pp,
233342
+ char **pzErrmsg,
233343
+ const char *zSql
233344
+){
233345
+ int rc = sqlite3_prepare_v2(db, zSql, -1, pp, 0);
233346
+ if( pzErrmsg && rc!=SQLITE_OK ){
233347
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
233348
+ }
233349
+ return rc;
233350
+}
231566233351
231567233352
/*
231568233353
** Formulate and prepare a SELECT statement to retrieve a row from table
231569233354
** zTab in database zDb based on its primary key. i.e.
231570233355
**
@@ -231583,16 +233368,16 @@
231583233368
const char *zTab, /* Table name */
231584233369
int bRowid,
231585233370
int nCol, /* Number of columns in table */
231586233371
const char **azCol, /* Names of table columns */
231587233372
u8 *abPK, /* PRIMARY KEY array */
231588
- sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
233373
+ sqlite3_stmt **ppStmt, /* OUT: Prepared SELECT statement */
233374
+ char **pzErrmsg /* OUT: Error message */
231589233375
){
231590233376
int rc = SQLITE_OK;
231591233377
char *zSql = 0;
231592233378
const char *zSep = "";
231593
- int nSql = -1;
231594233379
int i;
231595233380
231596233381
SessionBuffer cols = {0, 0, 0};
231597233382
SessionBuffer nooptest = {0, 0, 0};
231598233383
SessionBuffer pkfield = {0, 0, 0};
@@ -231668,11 +233453,11 @@
231668233453
nSql = buf.nBuf;
231669233454
}
231670233455
#endif
231671233456
231672233457
if( rc==SQLITE_OK ){
231673
- rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0);
233458
+ rc = sessionPrepare(db, ppStmt, pzErrmsg, zSql);
231674233459
}
231675233460
sqlite3_free(zSql);
231676233461
sqlite3_free(nooptest.aBuf);
231677233462
sqlite3_free(pkfield.aBuf);
231678233463
sqlite3_free(pkvar.aBuf);
@@ -231832,11 +233617,11 @@
231832233617
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
231833233618
231834233619
/* Build and compile a statement to execute: */
231835233620
if( rc==SQLITE_OK ){
231836233621
rc = sessionSelectStmt(db, 0, pSession->zDb,
231837
- zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel
233622
+ zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel, 0
231838233623
);
231839233624
}
231840233625
231841233626
nNoop = buf.nBuf;
231842233627
for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
@@ -233041,10 +234826,11 @@
233041234826
SessionBuffer rebase; /* Rebase information (if any) here */
233042234827
u8 bRebaseStarted; /* If table header is already in rebase */
233043234828
u8 bRebase; /* True to collect rebase information */
233044234829
u8 bIgnoreNoop; /* True to ignore no-op conflicts */
233045234830
int bRowid;
234831
+ char *zErr; /* Error message, if any */
233046234832
};
233047234833
233048234834
/* Number of prepared UPDATE statements to cache. */
233049234835
#define SESSION_UPDATE_CACHE_SZ 12
233050234836
@@ -233266,11 +235052,11 @@
233266235052
}
233267235053
sessionAppendStr(&buf, ")", &rc);
233268235054
}
233269235055
233270235056
if( rc==SQLITE_OK ){
233271
- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
235057
+ rc = sessionPrepare(db, &p->pDelete, &p->zErr, (char*)buf.aBuf);
233272235058
}
233273235059
sqlite3_free(buf.aBuf);
233274235060
233275235061
return rc;
233276235062
}
@@ -233293,11 +235079,11 @@
233293235079
const char *zTab, /* Table name */
233294235080
SessionApplyCtx *p /* Session changeset-apply context */
233295235081
){
233296235082
/* TODO */
233297235083
return sessionSelectStmt(db, p->bIgnoreNoop,
233298
- "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect
235084
+ "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect, &p->zErr
233299235085
);
233300235086
}
233301235087
233302235088
/*
233303235089
** Formulate and prepare an INSERT statement to add a record to table zTab.
@@ -233330,37 +235116,33 @@
233330235116
sessionAppendStr(&buf, ", ?", &rc);
233331235117
}
233332235118
sessionAppendStr(&buf, ")", &rc);
233333235119
233334235120
if( rc==SQLITE_OK ){
233335
- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
235121
+ rc = sessionPrepare(db, &p->pInsert, &p->zErr, (char*)buf.aBuf);
233336235122
}
233337235123
sqlite3_free(buf.aBuf);
233338235124
return rc;
233339235125
}
233340235126
233341
-static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){
233342
- return sqlite3_prepare_v2(db, zSql, -1, pp, 0);
233343
-}
233344
-
233345235127
/*
233346235128
** Prepare statements for applying changes to the sqlite_stat1 table.
233347235129
** These are similar to those created by sessionSelectRow(),
233348235130
** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
233349235131
** other tables.
233350235132
*/
233351235133
static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
233352235134
int rc = sessionSelectRow(db, "sqlite_stat1", p);
233353235135
if( rc==SQLITE_OK ){
233354
- rc = sessionPrepare(db, &p->pInsert,
235136
+ rc = sessionPrepare(db, &p->pInsert, 0,
233355235137
"INSERT INTO main.sqlite_stat1 VALUES(?1, "
233356235138
"CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, "
233357235139
"?3)"
233358235140
);
233359235141
}
233360235142
if( rc==SQLITE_OK ){
233361
- rc = sessionPrepare(db, &p->pDelete,
235143
+ rc = sessionPrepare(db, &p->pDelete, 0,
233362235144
"DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
233363235145
"CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
233364235146
"AND (?4 OR stat IS ?3)"
233365235147
);
233366235148
}
@@ -233580,11 +235362,11 @@
233580235362
sqlite3_changeset_iter *pIter, /* Changeset iterator */
233581235363
int(*xConflict)(void *, int, sqlite3_changeset_iter*),
233582235364
void *pCtx, /* First argument for conflict handler */
233583235365
int *pbReplace /* OUT: Set to true if PK row is found */
233584235366
){
233585
- int res = 0; /* Value returned by conflict handler */
235367
+ int res = SQLITE_CHANGESET_OMIT;/* Value returned by conflict handler */
233586235368
int rc;
233587235369
int nCol;
233588235370
int op;
233589235371
const char *zDummy;
233590235372
@@ -233601,15 +235383,13 @@
233601235383
rc = SQLITE_OK;
233602235384
}
233603235385
233604235386
if( rc==SQLITE_ROW ){
233605235387
/* There exists another row with the new.* primary key. */
233606
- if( p->bIgnoreNoop
233607
- && sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
235388
+ if( 0==p->bIgnoreNoop
235389
+ || 0==sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
233608235390
){
233609
- res = SQLITE_CHANGESET_OMIT;
233610
- }else{
233611235391
pIter->pConflict = p->pSelect;
233612235392
res = xConflict(pCtx, eType, pIter);
233613235393
pIter->pConflict = 0;
233614235394
}
233615235395
rc = sqlite3_reset(p->pSelect);
@@ -233619,11 +235399,13 @@
233619235399
** to the SessionApplyCtx.constraints buffer. */
233620235400
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
233621235401
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
233622235402
sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
233623235403
return SQLITE_OK;
233624
- }else{
235404
+ }else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE
235405
+ || eType==SQLITE_CHANGESET_CONFLICT
235406
+ ){
233625235407
/* No other row with the new.* primary key. */
233626235408
res = xConflict(pCtx, eType+1, pIter);
233627235409
if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
233628235410
}
233629235411
}
@@ -233717,11 +235499,11 @@
233717235499
}
233718235500
if( rc!=SQLITE_OK ) return rc;
233719235501
233720235502
sqlite3_step(p->pDelete);
233721235503
rc = sqlite3_reset(p->pDelete);
233722
- if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){
235504
+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
233723235505
rc = sessionConflictHandler(
233724235506
SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
233725235507
);
233726235508
}else if( (rc&0xff)==SQLITE_CONSTRAINT ){
233727235509
rc = sessionConflictHandler(
@@ -234122,10 +235904,11 @@
234122235904
}
234123235905
}
234124235906
234125235907
assert( sApply.bRebase || sApply.rebase.nBuf==0 );
234126235908
if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
235909
+ assert( ppRebase!=0 && pnRebase!=0 );
234127235910
*ppRebase = (void*)sApply.rebase.aBuf;
234128235911
*pnRebase = sApply.rebase.nBuf;
234129235912
sApply.rebase.aBuf = 0;
234130235913
}
234131235914
sessionUpdateFree(&sApply);
@@ -234139,10 +235922,15 @@
234139235922
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
234140235923
assert( db->flags & SQLITE_FkNoAction );
234141235924
db->flags &= ~((u64)SQLITE_FkNoAction);
234142235925
db->aDb[0].pSchema->schema_cookie -= 32;
234143235926
}
235927
+
235928
+ assert( rc!=SQLITE_OK || sApply.zErr==0 );
235929
+ sqlite3_set_errmsg(db, rc, sApply.zErr);
235930
+ sqlite3_free(sApply.zErr);
235931
+
234144235932
sqlite3_mutex_leave(sqlite3_db_mutex(db));
234145235933
return rc;
234146235934
}
234147235935
234148235936
/*
@@ -236348,25 +238136,18 @@
236348238136
** Constants for the largest and smallest possible 64-bit signed integers.
236349238137
*/
236350238138
# define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
236351238139
# define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
236352238140
236353
-/* The uptr type is an unsigned integer large enough to hold a pointer
238141
+/*
238142
+** This macro is used in a single assert() within fts5 to check that an
238143
+** allocation is aligned to an 8-byte boundary. But it is a complicated
238144
+** macro to get right for multiple platforms without generating warnings.
238145
+** So instead of reproducing the entire definition from sqliteInt.h, we
238146
+** just do without this assert() for the rare non-amalgamation builds.
236354238147
*/
236355
-#if defined(HAVE_STDINT_H)
236356
- typedef uintptr_t uptr;
236357
-#elif SQLITE_PTRSIZE==4
236358
- typedef u32 uptr;
236359
-#else
236360
- typedef u64 uptr;
236361
-#endif
236362
-
236363
-#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
236364
-# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
236365
-#else
236366
-# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
236367
-#endif
238148
+#define EIGHT_BYTE_ALIGNMENT(x) 1
236368238149
236369238150
/*
236370238151
** Macros needed to provide flexible arrays in a portable way
236371238152
*/
236372238153
#ifndef offsetof
@@ -237110,11 +238891,11 @@
237110238891
** ){
237111238892
** // The document with rowid iRowid matches the expression!
237112238893
** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
237113238894
** }
237114238895
*/
237115
-static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, int bDesc);
238896
+static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, i64, int bDesc);
237116238897
static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax);
237117238898
static int sqlite3Fts5ExprEof(Fts5Expr*);
237118238899
static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
237119238900
237120238901
static void sqlite3Fts5ExprFree(Fts5Expr*);
@@ -242679,11 +244460,17 @@
242679244460
** equal to iFirst.
242680244461
**
242681244462
** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
242682244463
** is not considered an error if the query does not match any documents.
242683244464
*/
242684
-static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
244465
+static int sqlite3Fts5ExprFirst(
244466
+ Fts5Expr *p,
244467
+ Fts5Index *pIdx,
244468
+ i64 iFirst,
244469
+ i64 iLast,
244470
+ int bDesc
244471
+){
242685244472
Fts5ExprNode *pRoot = p->pRoot;
242686244473
int rc; /* Return code */
242687244474
242688244475
p->pIndex = pIdx;
242689244476
p->bDesc = bDesc;
@@ -242700,10 +244487,13 @@
242700244487
242701244488
/* If the iterator is not at a real match, skip forward until it is. */
242702244489
while( pRoot->bNomatch && rc==SQLITE_OK ){
242703244490
assert( pRoot->bEof==0 );
242704244491
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
244492
+ }
244493
+ if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){
244494
+ pRoot->bEof = 1;
242705244495
}
242706244496
return rc;
242707244497
}
242708244498
242709244499
/*
@@ -245876,13 +247666,13 @@
245876247666
** backing store corruption. */
245877247667
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
245878247668
245879247669
if( rc==SQLITE_OK ){
245880247670
u8 *aOut = 0; /* Read blob data into this buffer */
245881
- int nByte = sqlite3_blob_bytes(p->pReader);
245882
- int szData = (sizeof(Fts5Data) + 7) & ~7;
245883
- sqlite3_int64 nAlloc = szData + nByte + FTS5_DATA_PADDING;
247671
+ i64 nByte = sqlite3_blob_bytes(p->pReader);
247672
+ i64 szData = (sizeof(Fts5Data) + 7) & ~7;
247673
+ i64 nAlloc = szData + nByte + FTS5_DATA_PADDING;
245884247674
pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
245885247675
if( pRet ){
245886247676
pRet->nn = nByte;
245887247677
aOut = pRet->p = (u8*)pRet + szData;
245888247678
}else{
@@ -251821,15 +253611,18 @@
251821253611
** function populates it with the initial structure objects for each index,
251822253612
** and the initial version of the "averages" record (a zero-byte blob).
251823253613
*/
251824253614
static int sqlite3Fts5IndexReinit(Fts5Index *p){
251825253615
Fts5Structure *pTmp;
251826
- u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
253616
+ union {
253617
+ Fts5Structure sFts;
253618
+ u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
253619
+ } uFts;
251827253620
fts5StructureInvalidate(p);
251828253621
fts5IndexDiscardData(p);
251829
- pTmp = (Fts5Structure*)tmpSpace;
251830
- memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
253622
+ pTmp = &uFts.sFts;
253623
+ memset(uFts.tmpSpace, 0, sizeof(uFts.tmpSpace));
251831253624
if( p->pConfig->bContentlessDelete ){
251832253625
pTmp->nOriginCntr = 1;
251833253626
}
251834253627
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
251835253628
fts5StructureWrite(p, pTmp);
@@ -255045,10 +256838,21 @@
255045256838
{
255046256839
pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
255047256840
}
255048256841
#endif
255049256842
}
256843
+
256844
+static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
256845
+#if SQLITE_VERSION_NUMBER>=3008002
256846
+#ifndef SQLITE_CORE
256847
+ if( sqlite3_libversion_number()>=3008002 )
256848
+#endif
256849
+ {
256850
+ pIdxInfo->estimatedRows = nRow;
256851
+ }
256852
+#endif
256853
+}
255050256854
255051256855
static int fts5UsePatternMatch(
255052256856
Fts5Config *pConfig,
255053256857
struct sqlite3_index_constraint *p
255054256858
){
@@ -255181,11 +256985,11 @@
255181256985
bSeenRank = 1;
255182256986
}else{
255183256987
nSeenMatch++;
255184256988
idxStr[iIdxStr++] = 'M';
255185256989
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
255186
- idxStr += strlen(&idxStr[iIdxStr]);
256990
+ iIdxStr += (int)strlen(&idxStr[iIdxStr]);
255187256991
assert( idxStr[iIdxStr]=='\0' );
255188256992
}
255189256993
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
255190256994
pInfo->aConstraintUsage[i].omit = 1;
255191256995
}
@@ -255200,10 +257004,11 @@
255200257004
nSeenMatch++;
255201257005
}else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
255202257006
idxStr[iIdxStr++] = '=';
255203257007
bSeenEq = 1;
255204257008
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
257009
+ pInfo->aConstraintUsage[i].omit = 1;
255205257010
}
255206257011
}
255207257012
}
255208257013
255209257014
if( bSeenEq==0 ){
@@ -255247,21 +257052,25 @@
255247257052
}
255248257053
}
255249257054
255250257055
/* Calculate the estimated cost based on the flags set in idxFlags. */
255251257056
if( bSeenEq ){
255252
- pInfo->estimatedCost = nSeenMatch ? 1000.0 : 10.0;
255253
- if( nSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
255254
- }else if( bSeenLt && bSeenGt ){
255255
- pInfo->estimatedCost = nSeenMatch ? 5000.0 : 250000.0;
255256
- }else if( bSeenLt || bSeenGt ){
255257
- pInfo->estimatedCost = nSeenMatch ? 7500.0 : 750000.0;
257057
+ pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
257058
+ fts5SetUniqueFlag(pInfo);
257059
+ fts5SetEstimatedRows(pInfo, 1);
255258257060
}else{
255259
- pInfo->estimatedCost = nSeenMatch ? 10000.0 : 1000000.0;
255260
- }
255261
- for(i=1; i<nSeenMatch; i++){
255262
- pInfo->estimatedCost *= 0.4;
257061
+ if( bSeenLt && bSeenGt ){
257062
+ pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0;
257063
+ }else if( bSeenLt || bSeenGt ){
257064
+ pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0;
257065
+ }else{
257066
+ pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
257067
+ }
257068
+ for(i=1; i<nSeenMatch; i++){
257069
+ pInfo->estimatedCost *= 0.4;
257070
+ }
257071
+ fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
255263257072
}
255264257073
255265257074
pInfo->idxNum = idxFlags;
255266257075
return SQLITE_OK;
255267257076
}
@@ -255456,11 +257265,13 @@
255456257265
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
255457257266
Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
255458257267
int bDesc = pCsr->bDesc;
255459257268
i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
255460257269
255461
- rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc);
257270
+ rc = sqlite3Fts5ExprFirst(
257271
+ pCsr->pExpr, pTab->p.pIndex, iRowid, pCsr->iLastRowid, bDesc
257272
+ );
255462257273
if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
255463257274
*pbSkip = 1;
255464257275
}
255465257276
255466257277
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
@@ -255628,11 +257439,13 @@
255628257439
}
255629257440
255630257441
static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
255631257442
int rc;
255632257443
Fts5Expr *pExpr = pCsr->pExpr;
255633
- rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc);
257444
+ rc = sqlite3Fts5ExprFirst(
257445
+ pExpr, pTab->p.pIndex, pCsr->iFirstRowid, pCsr->iLastRowid, bDesc
257446
+ );
255634257447
if( sqlite3Fts5ExprEof(pExpr) ){
255635257448
CsrFlagSet(pCsr, FTS5CSR_EOF);
255636257449
}
255637257450
fts5CsrNewrow(pCsr);
255638257451
return rc;
@@ -258113,11 +259926,11 @@
258113259926
int nArg, /* Number of args */
258114259927
sqlite3_value **apUnused /* Function arguments */
258115259928
){
258116259929
assert( nArg==0 );
258117259930
UNUSED_PARAM2(nArg, apUnused);
258118
- sqlite3_result_text(pCtx, "fts5: 2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839", -1, SQLITE_TRANSIENT);
259931
+ sqlite3_result_text(pCtx, "fts5: 2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70", -1, SQLITE_TRANSIENT);
258119259932
}
258120259933
258121259934
/*
258122259935
** Implementation of fts5_locale(LOCALE, TEXT) function.
258123259936
**
@@ -258136,13 +259949,13 @@
258136259949
sqlite3_context *pCtx, /* Function call context */
258137259950
int nArg, /* Number of args */
258138259951
sqlite3_value **apArg /* Function arguments */
258139259952
){
258140259953
const char *zLocale = 0;
258141
- int nLocale = 0;
259954
+ i64 nLocale = 0;
258142259955
const char *zText = 0;
258143
- int nText = 0;
259956
+ i64 nText = 0;
258144259957
258145259958
assert( nArg==2 );
258146259959
UNUSED_PARAM(nArg);
258147259960
258148259961
zLocale = (const char*)sqlite3_value_text(apArg[0]);
@@ -258155,14 +259968,14 @@
258155259968
sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
258156259969
}else{
258157259970
Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx);
258158259971
u8 *pBlob = 0;
258159259972
u8 *pCsr = 0;
258160
- int nBlob = 0;
259973
+ i64 nBlob = 0;
258161259974
258162259975
nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText;
258163
- pBlob = (u8*)sqlite3_malloc(nBlob);
259976
+ pBlob = (u8*)sqlite3_malloc64(nBlob);
258164259977
if( pBlob==0 ){
258165259978
sqlite3_result_error_nomem(pCtx);
258166259979
return;
258167259980
}
258168259981
258169259982
--- 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 ** 9f184f8dfa5ef6d57e10376adc30e0060ced with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -168,11 +168,13 @@
168 #define SQLITE_OMIT_LOAD_EXTENSION 1
169 #define SQLITE_ENABLE_LOCKING_STYLE 0
170 #define HAVE_UTIME 1
171 #else
172 /* This is not VxWorks. */
173 #define OS_VXWORKS 0
 
 
174 #define HAVE_FCHOWN 1
175 #define HAVE_READLINK 1
176 #define HAVE_LSTAT 1
177 #endif /* defined(_WRS_KERNEL) */
178
@@ -465,11 +467,14 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.51.0"
469 #define SQLITE_VERSION_NUMBER 3051000
470 #define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
 
 
 
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -814,10 +819,13 @@
814 ** [sqlite3_extended_errcode()].
815 */
816 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
817 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
818 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
 
 
 
819 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
820 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
821 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
822 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
823 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -848,10 +856,12 @@
848 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
849 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
850 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
851 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
852 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
 
 
853 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
854 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
855 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
856 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
857 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -2652,21 +2662,24 @@
2652 ** views in the main database schema or in the schemas of ATTACH-ed
2653 ** databases.)^ </dd>
2654 **
2655 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
2656 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2657 ** <dd> ^This option is used to enable or disable the
2658 ** [fts3_tokenizer()] function which is part of the
2659 ** [FTS3] full-text search engine extension.
2660 ** There must be two additional arguments.
2661 ** The first argument is an integer which is 0 to disable fts3_tokenizer() or
2662 ** positive to enable fts3_tokenizer() or negative to leave the setting
2663 ** unchanged.
2664 ** The second parameter is a pointer to an integer into which
2665 ** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
2666 ** following this call. The second parameter may be a NULL pointer, in
2667 ** which case the new setting is not reported back. </dd>
 
 
 
2668 **
2669 ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
2670 ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
2671 ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
2672 ** interface independently of the [load_extension()] SQL function.
@@ -4512,10 +4525,38 @@
4512 SQLITE_API const char *sqlite3_errmsg(sqlite3*);
4513 SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
4514 SQLITE_API const char *sqlite3_errstr(int);
4515 SQLITE_API int sqlite3_error_offset(sqlite3 *db);
4516
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4517 /*
4518 ** CAPI3REF: Prepared Statement Object
4519 ** KEYWORDS: {prepared statement} {prepared statements}
4520 **
4521 ** An instance of this object represents a single SQL statement that
@@ -6522,10 +6563,11 @@
6522 ** to be attached to [database connection] D using name N. Subsequent
6523 ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
6524 ** or a NULL pointer if there were no prior calls to
6525 ** sqlite3_set_clientdata() with the same values of D and N.
6526 ** Names are compared using strcmp() and are thus case sensitive.
 
6527 **
6528 ** If P and X are both non-NULL, then the destructor X is invoked with
6529 ** argument P on the first of the following occurrences:
6530 ** <ul>
6531 ** <li> An out-of-memory error occurs during the call to
@@ -9197,14 +9239,23 @@
9197 ** the resetFlg is true, then the highest instantaneous value is
9198 ** reset back down to the current value.
9199 **
9200 ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
9201 ** non-zero [error code] on failure.
 
 
 
 
 
 
 
 
9202 **
9203 ** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
9204 */
9205 SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
 
9206
9207 /*
9208 ** CAPI3REF: Status Parameters for database connections
9209 ** KEYWORDS: {SQLITE_DBSTATUS options}
9210 **
@@ -9297,10 +9348,14 @@
9297 ** database file in rollback mode databases. Any pages written as part of
9298 ** transaction rollback or database recovery operations are not included.
9299 ** If an IO or other error occurs while writing a page to disk, the effect
9300 ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
9301 ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
 
 
 
 
9302 ** </dd>
9303 **
9304 ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
9305 ** <dd>This parameter returns the number of dirty cache entries that have
9306 ** been written to disk in the middle of a transaction due to the page
@@ -9312,10 +9367,22 @@
9312 **
9313 ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
9314 ** <dd>This parameter returns zero for the current value if and only if
9315 ** all foreign key constraints (deferred or immediate) have been
9316 ** resolved.)^ ^The highwater mark is always 0.
 
 
 
 
 
 
 
 
 
 
 
 
9317 ** </dd>
9318 ** </dl>
9319 */
9320 #define SQLITE_DBSTATUS_LOOKASIDE_USED 0
9321 #define SQLITE_DBSTATUS_CACHE_USED 1
@@ -9328,11 +9395,12 @@
9328 #define SQLITE_DBSTATUS_CACHE_MISS 8
9329 #define SQLITE_DBSTATUS_CACHE_WRITE 9
9330 #define SQLITE_DBSTATUS_DEFERRED_FKS 10
9331 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
9332 #define SQLITE_DBSTATUS_CACHE_SPILL 12
9333 #define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
 
9334
9335
9336 /*
9337 ** CAPI3REF: Prepared Statement Status
9338 ** METHOD: sqlite3_stmt
@@ -10093,25 +10161,38 @@
10093 ** ^The third parameter is the name of the database that was written to -
10094 ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
10095 ** is the number of pages currently in the write-ahead log file,
10096 ** including those that were just committed.
10097 **
10098 ** The callback function should normally return [SQLITE_OK]. ^If an error
10099 ** code is returned, that error will propagate back up through the
10100 ** SQLite code base to cause the statement that provoked the callback
10101 ** to report an error, though the commit will have still occurred. If the
10102 ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
10103 ** that does not correspond to any valid SQLite error code, the results
10104 ** are undefined.
10105 **
10106 ** A single database handle may have at most a single write-ahead log callback
10107 ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
10108 ** previously registered write-ahead log callback. ^The return value is
10109 ** a copy of the third parameter from the previous call, if any, or 0.
10110 ** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
10111 ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
10112 ** overwrite any prior [sqlite3_wal_hook()] settings.
 
 
 
 
 
 
 
 
 
 
 
 
 
10113 */
10114 SQLITE_API void *sqlite3_wal_hook(
10115 sqlite3*,
10116 int(*)(void *,sqlite3*,const char*,int),
10117 void*
@@ -10124,11 +10205,11 @@
10124 ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
10125 ** [sqlite3_wal_hook()] that causes any database on [database connection] D
10126 ** to automatically [checkpoint]
10127 ** after committing a transaction if there are N or
10128 ** more frames in the [write-ahead log] file. ^Passing zero or
10129 ** a negative value as the nFrame parameter disables automatic
10130 ** checkpoints entirely.
10131 **
10132 ** ^The callback registered by this function replaces any existing callback
10133 ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback
10134 ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
@@ -10140,13 +10221,14 @@
10140 ** ^Checkpoints initiated by this mechanism are
10141 ** [sqlite3_wal_checkpoint_v2|PASSIVE].
10142 **
10143 ** ^Every new [database connection] defaults to having the auto-checkpoint
10144 ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
10145 ** pages. The use of this interface
10146 ** is only necessary if the default setting is found to be suboptimal
10147 ** for a particular application.
 
10148 */
10149 SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
10150
10151 /*
10152 ** CAPI3REF: Checkpoint a database
@@ -10207,10 +10289,15 @@
10207 **
10208 ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
10209 ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
10210 ** addition that it also truncates the log file to zero bytes just prior
10211 ** to a successful return.
 
 
 
 
 
10212 ** </dl>
10213 **
10214 ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
10215 ** the log file or to -1 if the checkpoint could not run because
10216 ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
@@ -10277,10 +10364,11 @@
10277 ** These constants define all valid values for the "checkpoint mode" passed
10278 ** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
10279 ** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
10280 ** meaning of each of these checkpoint modes.
10281 */
 
10282 #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
10283 #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
10284 #define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
10285 #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
10286
@@ -11104,11 +11192,11 @@
11104 ** to avoid a memory leak.
11105 **
11106 ** The [sqlite3_snapshot_get()] interface is only available when the
11107 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
11108 */
11109 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
11110 sqlite3 *db,
11111 const char *zSchema,
11112 sqlite3_snapshot **ppSnapshot
11113 );
11114
@@ -11153,11 +11241,11 @@
11153 ** database connection in order to make it ready to use snapshots.)
11154 **
11155 ** The [sqlite3_snapshot_open()] interface is only available when the
11156 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
11157 */
11158 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
11159 sqlite3 *db,
11160 const char *zSchema,
11161 sqlite3_snapshot *pSnapshot
11162 );
11163
@@ -11170,11 +11258,11 @@
11170 ** using this routine to avoid a memory leak.
11171 **
11172 ** The [sqlite3_snapshot_free()] interface is only available when the
11173 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
11174 */
11175 SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
11176
11177 /*
11178 ** CAPI3REF: Compare the ages of two snapshot handles.
11179 ** METHOD: sqlite3_snapshot
11180 **
@@ -11197,11 +11285,11 @@
11197 ** snapshot, and a positive value if P1 is a newer snapshot than P2.
11198 **
11199 ** This interface is only available if SQLite is compiled with the
11200 ** [SQLITE_ENABLE_SNAPSHOT] option.
11201 */
11202 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
11203 sqlite3_snapshot *p1,
11204 sqlite3_snapshot *p2
11205 );
11206
11207 /*
@@ -11225,11 +11313,11 @@
11225 ** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
11226 **
11227 ** This interface is only available if SQLite is compiled with the
11228 ** [SQLITE_ENABLE_SNAPSHOT] option.
11229 */
11230 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
11231
11232 /*
11233 ** CAPI3REF: Serialize a database
11234 **
11235 ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
@@ -11299,16 +11387,17 @@
11299 /*
11300 ** CAPI3REF: Deserialize a database
11301 **
11302 ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
11303 ** [database connection] D to disconnect from database S and then
11304 ** reopen S as an in-memory database based on the serialization contained
11305 ** in P. The serialized database P is N bytes in size. M is the size of
11306 ** the buffer P, which might be larger than N. If M is larger than N, and
11307 ** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
11308 ** permitted to add content to the in-memory database as long as the total
11309 ** size does not exceed M bytes.
 
11310 **
11311 ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
11312 ** invoke sqlite3_free() on the serialization buffer when the database
11313 ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
11314 ** SQLite will try to increase the buffer size using sqlite3_realloc64()
@@ -11371,10 +11460,56 @@
11371 */
11372 #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
11373 #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
11374 #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
11375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11376 /*
11377 ** Undo the hack that converts floating point types to integer for
11378 ** builds on processors without floating point support.
11379 */
11380 #ifdef SQLITE_OMIT_FLOATING_POINT
@@ -12629,10 +12764,19 @@
12629 ** CAPI3REF: Apply A Changeset To A Database
12630 **
12631 ** Apply a changeset or patchset to a database. These functions attempt to
12632 ** update the "main" database attached to handle db with the changes found in
12633 ** the changeset passed via the second and third arguments.
 
 
 
 
 
 
 
 
 
12634 **
12635 ** The fourth argument (xFilter) passed to these functions is the "filter
12636 ** callback". This may be passed NULL, in which case all changes in the
12637 ** changeset are applied to the database. For sqlite3changeset_apply() and
12638 ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
@@ -12767,16 +12911,10 @@
12767 ** It is safe to execute SQL statements, including those that write to the
12768 ** table that the callback related to, from within the xConflict callback.
12769 ** This can be used to further customize the application's conflict
12770 ** resolution strategy.
12771 **
12772 ** All changes made by these functions are enclosed in a savepoint transaction.
12773 ** If any other error (aside from a constraint failure when attempting to
12774 ** write to the target database) occurs, then the savepoint transaction is
12775 ** rolled back, restoring the target database to its original state, and an
12776 ** SQLite error code returned.
12777 **
12778 ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
12779 ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
12780 ** may set (*ppRebase) to point to a "rebase" that may be used with the
12781 ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
12782 ** is set to the size of the buffer in bytes. It is the responsibility of the
@@ -14351,11 +14489,11 @@
14351
14352 /*
14353 ** Maximum number of pages in one database file.
14354 **
14355 ** This is really just the default value for the max_page_count pragma.
14356 ** This value can be lowered (or raised) at run-time using that the
14357 ** max_page_count macro.
14358 */
14359 #ifndef SQLITE_MAX_PAGE_COUNT
14360 # define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
14361 #endif
@@ -15947,12 +16085,12 @@
15947 **
15948 ** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application
15949 ** must provide its own VFS implementation together with sqlite3_os_init()
15950 ** and sqlite3_os_end() routines.
15951 */
15952 #if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \
15953 !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN)
15954 # if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
15955 defined(__MINGW32__) || defined(__BORLANDC__)
15956 # define SQLITE_OS_WIN 1
15957 # define SQLITE_OS_UNIX 0
15958 # else
@@ -17450,10 +17588,13 @@
17450 #ifndef SQLITE_OMIT_TRACE
17451 SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
17452 #endif
17453 SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
17454 SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*);
 
 
 
17455
17456 SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*);
17457 SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
17458 SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
17459 SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
@@ -18122,11 +18263,11 @@
18122 struct sqlite3InitInfo { /* Information used during initialization */
18123 Pgno newTnum; /* Rootpage of table being initialized */
18124 u8 iDb; /* Which db file is being initialized */
18125 u8 busy; /* TRUE if currently initializing */
18126 unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
18127 unsigned imposterTable : 1; /* Building an imposter table */
18128 unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
18129 const char **azInit; /* "type", "name", and "tbl_name" columns */
18130 } init;
18131 int nVdbeActive; /* Number of VDBEs currently running */
18132 int nVdbeRead; /* Number of active VDBEs that read or write */
@@ -18205,10 +18346,11 @@
18205 int nStatement; /* Number of nested statement-transactions */
18206 i64 nDeferredCons; /* Net deferred constraints this transaction. */
18207 i64 nDeferredImmCons; /* Net deferred immediate constraints */
18208 int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
18209 DbClientData *pDbData; /* sqlite3_set_clientdata() content */
 
18210 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
18211 /* The following variables are all protected by the STATIC_MAIN
18212 ** mutex, not by sqlite3.mutex. They are used by code in notify.c.
18213 **
18214 ** When X.pUnlockConnection==Y, that means that X is waiting for Y to
@@ -18915,10 +19057,11 @@
18915 #define TF_Shadow 0x00001000 /* True for a shadow table */
18916 #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */
18917 #define TF_Ephemeral 0x00004000 /* An ephemeral table */
18918 #define TF_Eponymous 0x00008000 /* An eponymous virtual table */
18919 #define TF_Strict 0x00010000 /* STRICT mode */
 
18920
18921 /*
18922 ** Allowed values for Table.eTabType
18923 */
18924 #define TABTYP_NORM 0 /* Ordinary table */
@@ -19503,10 +19646,11 @@
19503 AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
19504 union {
19505 Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
19506 ** for a column of an index on an expression */
19507 Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
 
19508 struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
19509 int iAddr; /* Subroutine entry address */
19510 int regReturn; /* Register used to hold return address */
19511 } sub;
19512 } y;
@@ -20080,10 +20224,11 @@
20080 #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
20081 #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
20082 #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
20083 #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
20084 #define SF_Correlated 0x20000000 /* True if references the outer context */
 
20085
20086 /* True if SrcItem X is a subquery that has SF_NestedFrom */
20087 #define IsNestedFrom(X) \
20088 ((X)->fg.isSubquery && \
20089 ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0)
@@ -20833,10 +20978,11 @@
20833 struct Table *pTab; /* Table of generated column */
20834 struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
20835 SrcItem *pSrcItem; /* A single FROM clause item */
20836 DbFixer *pFix; /* See sqlite3FixSelect() */
20837 Mem *aMem; /* See sqlite3BtreeCursorHint() */
 
20838 } u;
20839 };
20840
20841 /*
20842 ** The following structure contains information used by the sqliteFix...
@@ -21540,10 +21686,11 @@
21540 SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
21541 #endif
21542 SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
21543 SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
21544 SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
 
21545 SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
21546 SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
21547 SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
21548 #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
21549 #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -21636,16 +21783,20 @@
21636 SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
21637 SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
21638 SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
21639 SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
21640 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
21641 SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3*);
21642 #endif
21643 SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
21644 SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
21645 SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
21646 SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p);
 
 
 
 
21647
21648 #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
21649 SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
21650 #endif
21651
@@ -22628,10 +22779,13 @@
22628 #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
22629 "ENABLE_BATCH_ATOMIC_WRITE",
22630 #endif
22631 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
22632 "ENABLE_BYTECODE_VTAB",
 
 
 
22633 #endif
22634 #ifdef SQLITE_ENABLE_CEROD
22635 "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
22636 #endif
22637 #ifdef SQLITE_ENABLE_COLUMN_METADATA
@@ -22718,10 +22872,13 @@
22718 #ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
22719 "ENABLE_ORDERED_SET_AGGREGATES",
22720 #endif
22721 #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
22722 "ENABLE_OVERSIZE_CELL_CHECK",
 
 
 
22723 #endif
22724 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
22725 "ENABLE_PREUPDATE_HOOK",
22726 #endif
22727 #ifdef SQLITE_ENABLE_QPSG
@@ -24202,11 +24359,14 @@
24202 Mem oldipk; /* Memory cell holding "old" IPK value */
24203 Mem *aNew; /* Array of new.* values */
24204 Table *pTab; /* Schema object being updated */
24205 Index *pPk; /* PK index if pTab is WITHOUT ROWID */
24206 sqlite3_value **apDflt; /* Array of default values, if required */
24207 u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
 
 
 
24208 };
24209
24210 /*
24211 ** An instance of this object is used to pass an vector of values into
24212 ** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -24366,13 +24526,15 @@
24366 SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
24367 SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
24368 #endif
24369
24370 #ifndef SQLITE_OMIT_FOREIGN_KEY
24371 SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int);
 
24372 #else
24373 # define sqlite3VdbeCheckFk(p,i) 0
 
24374 #endif
24375
24376 #ifdef SQLITE_DEBUG
24377 SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
24378 SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
@@ -24577,27 +24739,29 @@
24577 }
24578
24579 /*
24580 ** Query status information for a single database connection
24581 */
24582 SQLITE_API int sqlite3_db_status(
24583 sqlite3 *db, /* The database connection whose status is desired */
24584 int op, /* Status verb */
24585 int *pCurrent, /* Write current value here */
24586 int *pHighwater, /* Write high-water mark here */
24587 int resetFlag /* Reset high-water mark if true */
24588 ){
24589 int rc = SQLITE_OK; /* Return code */
24590 #ifdef SQLITE_ENABLE_API_ARMOR
24591 if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){
24592 return SQLITE_MISUSE_BKPT;
24593 }
24594 #endif
24595 sqlite3_mutex_enter(db->mutex);
24596 switch( op ){
24597 case SQLITE_DBSTATUS_LOOKASIDE_USED: {
24598 *pCurrent = sqlite3LookasideUsed(db, pHighwater);
 
 
24599 if( resetFlag ){
24600 LookasideSlot *p = db->lookaside.pFree;
24601 if( p ){
24602 while( p->pNext ) p = p->pNext;
24603 p->pNext = db->lookaside.pInit;
@@ -24624,11 +24788,11 @@
24624 testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
24625 testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
24626 assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
24627 assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
24628 *pCurrent = 0;
24629 *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
24630 if( resetFlag ){
24631 db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
24632 }
24633 break;
24634 }
@@ -24638,11 +24802,11 @@
24638 ** by all pagers associated with the given database connection. The
24639 ** highwater mark is meaningless and is returned as zero.
24640 */
24641 case SQLITE_DBSTATUS_CACHE_USED_SHARED:
24642 case SQLITE_DBSTATUS_CACHE_USED: {
24643 int totalUsed = 0;
24644 int i;
24645 sqlite3BtreeEnterAll(db);
24646 for(i=0; i<db->nDb; i++){
24647 Btree *pBt = db->aDb[i].pBt;
24648 if( pBt ){
@@ -24654,22 +24818,22 @@
24654 totalUsed += nByte;
24655 }
24656 }
24657 sqlite3BtreeLeaveAll(db);
24658 *pCurrent = totalUsed;
24659 *pHighwater = 0;
24660 break;
24661 }
24662
24663 /*
24664 ** *pCurrent gets an accurate estimate of the amount of memory used
24665 ** to store the schema for all databases (main, temp, and any ATTACHed
24666 ** databases. *pHighwater is set to zero.
24667 */
24668 case SQLITE_DBSTATUS_SCHEMA_USED: {
24669 int i; /* Used to iterate through schemas */
24670 int nByte = 0; /* Used to accumulate return value */
24671
24672 sqlite3BtreeEnterAll(db);
24673 db->pnBytesFreed = &nByte;
24674 assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
24675 db->lookaside.pEnd = db->lookaside.pStart;
@@ -24699,19 +24863,19 @@
24699 }
24700 db->pnBytesFreed = 0;
24701 db->lookaside.pEnd = db->lookaside.pTrueEnd;
24702 sqlite3BtreeLeaveAll(db);
24703
24704 *pHighwater = 0;
24705 *pCurrent = nByte;
24706 break;
24707 }
24708
24709 /*
24710 ** *pCurrent gets an accurate estimate of the amount of memory used
24711 ** to store all prepared statements.
24712 ** *pHighwater is set to zero.
24713 */
24714 case SQLITE_DBSTATUS_STMT_USED: {
24715 struct Vdbe *pVdbe; /* Used to iterate through VMs */
24716 int nByte = 0; /* Used to accumulate return value */
24717
@@ -24722,19 +24886,19 @@
24722 sqlite3VdbeDelete(pVdbe);
24723 }
24724 db->lookaside.pEnd = db->lookaside.pTrueEnd;
24725 db->pnBytesFreed = 0;
24726
24727 *pHighwater = 0; /* IMP: R-64479-57858 */
24728 *pCurrent = nByte;
24729
24730 break;
24731 }
24732
24733 /*
24734 ** Set *pCurrent to the total cache hits or misses encountered by all
24735 ** pagers the database handle is connected to. *pHighwater is always set
24736 ** to zero.
24737 */
24738 case SQLITE_DBSTATUS_CACHE_SPILL:
24739 op = SQLITE_DBSTATUS_CACHE_WRITE+1;
24740 /* no break */ deliberate_fall_through
@@ -24750,23 +24914,43 @@
24750 if( db->aDb[i].pBt ){
24751 Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
24752 sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
24753 }
24754 }
24755 *pHighwater = 0; /* IMP: R-42420-56072 */
24756 /* IMP: R-54100-20147 */
24757 /* IMP: R-29431-39229 */
24758 *pCurrent = (int)nRet & 0x7fffffff;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24759 break;
24760 }
24761
24762 /* Set *pCurrent to non-zero if there are unresolved deferred foreign
24763 ** key constraints. Set *pCurrent to zero if all foreign key constraints
24764 ** have been satisfied. The *pHighwater is always set to zero.
24765 */
24766 case SQLITE_DBSTATUS_DEFERRED_FKS: {
24767 *pHighwater = 0; /* IMP: R-11967-56545 */
24768 *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
24769 break;
24770 }
24771
24772 default: {
@@ -24774,10 +24958,35 @@
24774 }
24775 }
24776 sqlite3_mutex_leave(db->mutex);
24777 return rc;
24778 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24779
24780 /************** End of status.c **********************************************/
24781 /************** Begin file date.c ********************************************/
24782 /*
24783 ** 2003 October 31
@@ -24967,10 +25176,14 @@
24967 if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
24968 return 1;
24969 }
24970 zDate += 5;
24971 p->tz = sgn*(nMn + nHr*60);
 
 
 
 
24972 zulu_time:
24973 while( sqlite3Isspace(*zDate) ){ zDate++; }
24974 return *zDate!=0;
24975 }
24976
@@ -26162,12 +26375,12 @@
26162 ** %j day of year 001-366
26163 ** %J ** julian day number
26164 ** %l hour 1-12 (leading zero converted to space)
26165 ** %m month 01-12
26166 ** %M minute 00-59
26167 ** %p "am" or "pm"
26168 ** %P "AM" or "PM"
26169 ** %R time as HH:MM
26170 ** %s seconds since 1970-01-01
26171 ** %S seconds 00-59
26172 ** %T time as HH:MM:SS
26173 ** %u day of week 1-7 Monday==1, Sunday==7
@@ -31770,56 +31983,74 @@
31770 etByte base; /* The base for radix conversion */
31771 etByte flags; /* One or more of FLAG_ constants below */
31772 etByte type; /* Conversion paradigm */
31773 etByte charset; /* Offset into aDigits[] of the digits string */
31774 etByte prefix; /* Offset into aPrefix[] of the prefix string */
 
31775 } et_info;
31776
31777 /*
31778 ** Allowed values for et_info.flags
31779 */
31780 #define FLAG_SIGNED 1 /* True if the value to convert is signed */
31781 #define FLAG_STRING 4 /* Allow infinite precision */
31782
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31783
31784 /*
31785 ** The following table is searched linearly, so it is good to put the
31786 ** most frequently used conversion types first.
31787 */
 
 
 
 
 
 
 
31788 static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
31789 static const char aPrefix[] = "-x0\000X0";
31790 static const et_info fmtinfo[] = {
31791 { 'd', 10, 1, etDECIMAL, 0, 0 },
31792 { 's', 0, 4, etSTRING, 0, 0 },
31793 { 'g', 0, 1, etGENERIC, 30, 0 },
31794 { 'z', 0, 4, etDYNSTRING, 0, 0 },
31795 { 'q', 0, 4, etESCAPE_q, 0, 0 },
31796 { 'Q', 0, 4, etESCAPE_Q, 0, 0 },
31797 { 'w', 0, 4, etESCAPE_w, 0, 0 },
31798 { 'c', 0, 0, etCHARX, 0, 0 },
31799 { 'o', 8, 0, etRADIX, 0, 2 },
31800 { 'u', 10, 0, etDECIMAL, 0, 0 },
31801 { 'x', 16, 0, etRADIX, 16, 1 },
31802 { 'X', 16, 0, etRADIX, 0, 4 },
31803 #ifndef SQLITE_OMIT_FLOATING_POINT
31804 { 'f', 0, 1, etFLOAT, 0, 0 },
31805 { 'e', 0, 1, etEXP, 30, 0 },
31806 { 'E', 0, 1, etEXP, 14, 0 },
31807 { 'G', 0, 1, etGENERIC, 14, 0 },
31808 #endif
31809 { 'i', 10, 1, etDECIMAL, 0, 0 },
31810 { 'n', 0, 0, etSIZE, 0, 0 },
31811 { '%', 0, 0, etPERCENT, 0, 0 },
31812 { 'p', 16, 0, etPOINTER, 0, 1 },
31813
31814 /* All the rest are undocumented and are for internal use only */
31815 { 'T', 0, 0, etTOKEN, 0, 0 },
31816 { 'S', 0, 0, etSRCITEM, 0, 0 },
31817 { 'r', 10, 1, etORDINAL, 0, 0 },
31818 };
31819
31820 /* Notes:
31821 **
31822 ** %S Takes a pointer to SrcItem. Shows name or database.name
31823 ** %!S Like %S but prefer the zName over the zAlias
31824 */
31825
@@ -31942,11 +32173,14 @@
31942 if( c!='%' ){
31943 bufpt = (char *)fmt;
31944 #if HAVE_STRCHRNUL
31945 fmt = strchrnul(fmt, '%');
31946 #else
31947 do{ fmt++; }while( *fmt && *fmt != '%' );
 
 
 
31948 #endif
31949 sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
31950 if( *fmt==0 ) break;
31951 }
31952 if( (c=(*++fmt))==0 ){
@@ -32056,19 +32290,36 @@
32056 }
32057 }
32058 }while( !done && (c=(*++fmt))!=0 );
32059
32060 /* Fetch the info entry for the field */
 
 
 
32061 infop = &fmtinfo[0];
32062 xtype = etINVALID;
32063 for(idx=0; idx<ArraySize(fmtinfo); idx++){
32064 if( c==fmtinfo[idx].fmttype ){
32065 infop = &fmtinfo[idx];
32066 xtype = infop->type;
32067 break;
32068 }
32069 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32070
32071 /*
32072 ** At this point, variables are initialized as follows:
32073 **
32074 ** flag_alternateform TRUE if a '#' is present.
@@ -32252,11 +32503,25 @@
32252 length = sqlite3Strlen30(bufpt);
32253 break;
32254 }
32255 }
32256 if( s.sign=='-' ){
32257 prefix = '-';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32258 }else{
32259 prefix = flag_prefix;
32260 }
32261
32262 exp = s.iDP-1;
@@ -33463,13 +33728,17 @@
33463 sqlite3StrAccumFinish(&x);
33464 sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
33465 n = 0;
33466 if( pItem->fg.isSubquery ) n++;
33467 if( pItem->fg.isTabFunc ) n++;
33468 if( pItem->fg.isUsing ) n++;
33469 if( pItem->fg.isUsing ){
33470 sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
 
 
 
 
33471 }
33472 if( pItem->fg.isSubquery ){
33473 assert( n==1 );
33474 if( pItem->pSTab ){
33475 Table *pTab = pItem->pSTab;
@@ -38108,11 +38377,11 @@
38108 static int kvstorageDelete(const char*, const char *zKey);
38109 static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
38110 #define KVSTORAGE_KEY_SZ 32
38111
38112 /* Expand the key name with an appropriate prefix and put the result
38113 ** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
38114 ** KVSTORAGE_KEY_SZ bytes.
38115 */
38116 static void kvstorageMakeKey(
38117 const char *zClass,
38118 const char *zKeyIn,
@@ -38167,14 +38436,16 @@
38167 ** enough to hold it all. The value put into zBuf must always be zero
38168 ** terminated, even if it gets truncated because nBuf is not large enough.
38169 **
38170 ** Return the total number of bytes in the data, without truncation, and
38171 ** not counting the final zero terminator. Return -1 if the key does
38172 ** not exist.
38173 **
38174 ** If nBuf<=0 then this routine simply returns the size of the data without
38175 ** actually reading it.
 
 
38176 */
38177 static int kvstorageRead(
38178 const char *zClass,
38179 const char *zKey,
38180 char *zBuf,
@@ -38219,15 +38490,13 @@
38219 /*
38220 ** An internal level of indirection which enables us to replace the
38221 ** kvvfs i/o methods with JavaScript implementations in WASM builds.
38222 ** Maintenance reminder: if this struct changes in any way, the JSON
38223 ** rendering of its structure must be updated in
38224 ** sqlite3_wasm_enum_json(). There are no binary compatibility
38225 ** concerns, so it does not need an iVersion member. This file is
38226 ** necessarily always compiled together with sqlite3_wasm_enum_json(),
38227 ** and JS code dynamically creates the mapping of members based on
38228 ** that JSON description.
38229 */
38230 typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
38231 struct sqlite3_kvvfs_methods {
38232 int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
38233 int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
@@ -38240,12 +38509,12 @@
38240 ** for JavaScript-side implementations in WASM builds. In such builds
38241 ** it cannot be const, but in native builds it should be so that
38242 ** the compiler can hopefully optimize this level of indirection out.
38243 ** That said, kvvfs is intended primarily for use in WASM builds.
38244 **
38245 ** Note that this is not explicitly flagged as static because the
38246 ** amalgamation build will tag it with SQLITE_PRIVATE.
38247 */
38248 #ifndef SQLITE_WASM
38249 const
38250 #endif
38251 SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
@@ -39414,14 +39683,15 @@
39414 #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
39415 aSyscall[13].pCurrent)
39416
39417 #if defined(HAVE_FCHMOD)
39418 { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
 
39419 #else
39420 { "fchmod", (sqlite3_syscall_ptr)0, 0 },
 
39421 #endif
39422 #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
39423
39424 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
39425 { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
39426 #else
39427 { "fallocate", (sqlite3_syscall_ptr)0, 0 },
@@ -39675,13 +39945,12 @@
39675 return fd;
39676 }
39677
39678 /*
39679 ** Helper functions to obtain and relinquish the global mutex. The
39680 ** global mutex is used to protect the unixInodeInfo and
39681 ** vxworksFileId objects used by this file, all of which may be
39682 ** shared by multiple threads.
39683 **
39684 ** Function unixMutexHeld() is used to assert() that the global mutex
39685 ** is held when required. This function is only used as part of assert()
39686 ** statements. e.g.
39687 **
@@ -39879,10 +40148,11 @@
39879 /*
39880 ** All unique filenames are held on a linked list headed by this
39881 ** variable:
39882 */
39883 static struct vxworksFileId *vxworksFileList = 0;
 
39884
39885 /*
39886 ** Simplify a filename into its canonical form
39887 ** by making the following changes:
39888 **
@@ -39944,47 +40214,47 @@
39944
39945 /* Search for an existing entry that matching the canonical name.
39946 ** If found, increment the reference count and return a pointer to
39947 ** the existing file ID.
39948 */
39949 unixEnterMutex();
39950 for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){
39951 if( pCandidate->nName==n
39952 && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0
39953 ){
39954 sqlite3_free(pNew);
39955 pCandidate->nRef++;
39956 unixLeaveMutex();
39957 return pCandidate;
39958 }
39959 }
39960
39961 /* No match was found. We will make a new file ID */
39962 pNew->nRef = 1;
39963 pNew->nName = n;
39964 pNew->pNext = vxworksFileList;
39965 vxworksFileList = pNew;
39966 unixLeaveMutex();
39967 return pNew;
39968 }
39969
39970 /*
39971 ** Decrement the reference count on a vxworksFileId object. Free
39972 ** the object when the reference count reaches zero.
39973 */
39974 static void vxworksReleaseFileId(struct vxworksFileId *pId){
39975 unixEnterMutex();
39976 assert( pId->nRef>0 );
39977 pId->nRef--;
39978 if( pId->nRef==0 ){
39979 struct vxworksFileId **pp;
39980 for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){}
39981 assert( *pp==pId );
39982 *pp = pId->pNext;
39983 sqlite3_free(pId);
39984 }
39985 unixLeaveMutex();
39986 }
39987 #endif /* OS_VXWORKS */
39988 /*************** End of Unique File ID Utility Used By VxWorks ****************
39989 ******************************************************************************/
39990
@@ -40368,10 +40638,14 @@
40368 do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
40369 if( rc!=1 ){
40370 storeLastErrno(pFile, errno);
40371 return SQLITE_IOERR;
40372 }
 
 
 
 
40373 rc = osFstat(fd, &statbuf);
40374 if( rc!=0 ){
40375 storeLastErrno(pFile, errno);
40376 return SQLITE_IOERR;
40377 }
@@ -40537,22 +40811,46 @@
40537 static int osSetPosixAdvisoryLock(
40538 int h, /* The file descriptor on which to take the lock */
40539 struct flock *pLock, /* The description of the lock */
40540 unixFile *pFile /* Structure holding timeout value */
40541 ){
40542 int tm = pFile->iBusyTimeout;
40543 int rc = osFcntl(h,F_SETLK,pLock);
40544 while( rc<0 && tm>0 ){
40545 /* On systems that support some kind of blocking file lock with a timeout,
40546 ** make appropriate changes here to invoke that blocking file lock. On
40547 ** generic posix, however, there is no such API. So we simply try the
40548 ** lock once every millisecond until either the timeout expires, or until
40549 ** the lock is obtained. */
40550 unixSleep(0,1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40551 rc = osFcntl(h,F_SETLK,pLock);
40552 tm--;
 
 
40553 }
 
40554 return rc;
40555 }
40556 #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
40557
40558
@@ -44869,14 +45167,21 @@
44869 #endif
44870
44871 storeLastErrno(pNew, 0);
44872 #if OS_VXWORKS
44873 if( rc!=SQLITE_OK ){
44874 if( h>=0 ) robust_close(pNew, h, __LINE__);
44875 h = -1;
44876 osUnlink(zFilename);
44877 pNew->ctrlFlags |= UNIXFILE_DELETE;
 
 
 
 
 
 
 
44878 }
44879 #endif
44880 if( rc!=SQLITE_OK ){
44881 if( h>=0 ) robust_close(pNew, h, __LINE__);
44882 }else{
@@ -44916,10 +45221,13 @@
44916 struct stat buf;
44917 const char *zDir = sqlite3_temp_directory;
44918
44919 while(1){
44920 if( zDir!=0
 
 
 
44921 && osStat(zDir, &buf)==0
44922 && S_ISDIR(buf.st_mode)
44923 && osAccess(zDir, 03)==0
44924 ){
44925 return zDir;
@@ -45229,10 +45537,16 @@
45229 assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
45230 || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
45231 || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
45232 || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
45233 );
 
 
 
 
 
 
45234
45235 /* Detect a pid change and reset the PRNG. There is a race condition
45236 ** here such that two or more threads all trying to open databases at
45237 ** the same instant might all reset the PRNG. But multiple resets
45238 ** are harmless.
@@ -45430,12 +45744,15 @@
45430 goto open_finished;
45431 }
45432 }
45433 #endif
45434
45435 assert( zPath==0 || zPath[0]=='/'
45436 || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL
 
 
 
45437 );
45438 rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
45439
45440 open_finished:
45441 if( rc!=SQLITE_OK ){
@@ -47160,10 +47477,13 @@
47160 }
47161 #ifdef SQLITE_OS_KV_OPTIONAL
47162 sqlite3KvvfsInit();
47163 #endif
47164 unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
 
 
 
47165
47166 #ifndef SQLITE_OMIT_WAL
47167 /* Validate lock assumptions */
47168 assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */
47169 assert( UNIX_SHM_BASE==120 ); /* Start of locking area */
@@ -47194,10 +47514,13 @@
47194 ** to release dynamically allocated objects. But not on unix.
47195 ** This routine is a no-op for unix.
47196 */
47197 SQLITE_API int sqlite3_os_end(void){
47198 unixBigLock = 0;
 
 
 
47199 return SQLITE_OK;
47200 }
47201
47202 #endif /* SQLITE_OS_UNIX */
47203
@@ -51192,204 +51515,10 @@
51192 ** on allocation size granularity boundaries.
51193 ** During sqlite3_os_init() we do a GetSystemInfo()
51194 ** to get the granularity size.
51195 */
51196 static SYSTEM_INFO winSysInfo;
51197
51198 #ifndef SQLITE_OMIT_WAL
51199
51200 /*
51201 ** Helper functions to obtain and relinquish the global mutex. The
51202 ** global mutex is used to protect the winLockInfo objects used by
51203 ** this file, all of which may be shared by multiple threads.
51204 **
51205 ** Function winShmMutexHeld() is used to assert() that the global mutex
51206 ** is held when required. This function is only used as part of assert()
51207 ** statements. e.g.
51208 **
51209 ** winShmEnterMutex()
51210 ** assert( winShmMutexHeld() );
51211 ** winShmLeaveMutex()
51212 */
51213 static sqlite3_mutex *winBigLock = 0;
51214 static void winShmEnterMutex(void){
51215 sqlite3_mutex_enter(winBigLock);
51216 }
51217 static void winShmLeaveMutex(void){
51218 sqlite3_mutex_leave(winBigLock);
51219 }
51220 #ifndef NDEBUG
51221 static int winShmMutexHeld(void) {
51222 return sqlite3_mutex_held(winBigLock);
51223 }
51224 #endif
51225
51226 /*
51227 ** Object used to represent a single file opened and mmapped to provide
51228 ** shared memory. When multiple threads all reference the same
51229 ** log-summary, each thread has its own winFile object, but they all
51230 ** point to a single instance of this object. In other words, each
51231 ** log-summary is opened only once per process.
51232 **
51233 ** winShmMutexHeld() must be true when creating or destroying
51234 ** this object or while reading or writing the following fields:
51235 **
51236 ** nRef
51237 ** pNext
51238 **
51239 ** The following fields are read-only after the object is created:
51240 **
51241 ** zFilename
51242 **
51243 ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
51244 ** winShmMutexHeld() is true when reading or writing any other field
51245 ** in this structure.
51246 **
51247 ** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate
51248 ** the *-shm file if the DMS-locking protocol demands it, and (c) map
51249 ** regions of the *-shm file into memory using MapViewOfFile() or
51250 ** similar. Other locks are taken by individual clients using the
51251 ** winShm.hShm handles.
51252 */
51253 struct winShmNode {
51254 sqlite3_mutex *mutex; /* Mutex to access this object */
51255 char *zFilename; /* Name of the file */
51256 HANDLE hSharedShm; /* File handle open on zFilename */
51257
51258 int isUnlocked; /* DMS lock has not yet been obtained */
51259 int isReadonly; /* True if read-only */
51260 int szRegion; /* Size of shared-memory regions */
51261 int nRegion; /* Size of array apRegion */
51262
51263 struct ShmRegion {
51264 HANDLE hMap; /* File handle from CreateFileMapping */
51265 void *pMap;
51266 } *aRegion;
51267 DWORD lastErrno; /* The Windows errno from the last I/O error */
51268
51269 int nRef; /* Number of winShm objects pointing to this */
51270 winShmNode *pNext; /* Next in list of all winShmNode objects */
51271 #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51272 u8 nextShmId; /* Next available winShm.id value */
51273 #endif
51274 };
51275
51276 /*
51277 ** A global array of all winShmNode objects.
51278 **
51279 ** The winShmMutexHeld() must be true while reading or writing this list.
51280 */
51281 static winShmNode *winShmNodeList = 0;
51282
51283 /*
51284 ** Structure used internally by this VFS to record the state of an
51285 ** open shared memory connection. There is one such structure for each
51286 ** winFile open on a wal mode database.
51287 */
51288 struct winShm {
51289 winShmNode *pShmNode; /* The underlying winShmNode object */
51290 u16 sharedMask; /* Mask of shared locks held */
51291 u16 exclMask; /* Mask of exclusive locks held */
51292 HANDLE hShm; /* File-handle on *-shm file. For locking. */
51293 int bReadonly; /* True if hShm is opened read-only */
51294 #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51295 u8 id; /* Id of this connection with its winShmNode */
51296 #endif
51297 };
51298
51299 /*
51300 ** Constants used for locking
51301 */
51302 #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
51303 #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
51304
51305 /* Forward references to VFS methods */
51306 static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
51307 static int winDelete(sqlite3_vfs *,const char*,int);
51308
51309 /*
51310 ** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
51311 **
51312 ** This is not a VFS shared-memory method; it is a utility function called
51313 ** by VFS shared-memory methods.
51314 */
51315 static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
51316 winShmNode **pp;
51317 winShmNode *p;
51318 assert( winShmMutexHeld() );
51319 OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
51320 osGetCurrentProcessId(), deleteFlag));
51321 pp = &winShmNodeList;
51322 while( (p = *pp)!=0 ){
51323 if( p->nRef==0 ){
51324 int i;
51325 if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
51326 for(i=0; i<p->nRegion; i++){
51327 BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
51328 OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
51329 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51330 UNUSED_VARIABLE_VALUE(bRc);
51331 bRc = osCloseHandle(p->aRegion[i].hMap);
51332 OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
51333 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51334 UNUSED_VARIABLE_VALUE(bRc);
51335 }
51336 winHandleClose(p->hSharedShm);
51337 if( deleteFlag ){
51338 SimulateIOErrorBenign(1);
51339 sqlite3BeginBenignMalloc();
51340 winDelete(pVfs, p->zFilename, 0);
51341 sqlite3EndBenignMalloc();
51342 SimulateIOErrorBenign(0);
51343 }
51344 *pp = p->pNext;
51345 sqlite3_free(p->aRegion);
51346 sqlite3_free(p);
51347 }else{
51348 pp = &p->pNext;
51349 }
51350 }
51351 }
51352
51353 /*
51354 ** The DMS lock has not yet been taken on the shm file associated with
51355 ** pShmNode. Take the lock. Truncate the *-shm file if required.
51356 ** Return SQLITE_OK if successful, or an SQLite error code otherwise.
51357 */
51358 static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
51359 HANDLE h = pShmNode->hSharedShm;
51360 int rc = SQLITE_OK;
51361
51362 assert( sqlite3_mutex_held(pShmNode->mutex) );
51363 rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
51364 if( rc==SQLITE_OK ){
51365 /* We have an EXCLUSIVE lock on the DMS byte. This means that this
51366 ** is the first process to open the file. Truncate it to zero bytes
51367 ** in this case. */
51368 if( pShmNode->isReadonly ){
51369 rc = SQLITE_READONLY_CANTINIT;
51370 }else{
51371 rc = winHandleTruncate(h, 0);
51372 }
51373
51374 /* Release the EXCLUSIVE lock acquired above. */
51375 winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
51376 }else if( (rc & 0xFF)==SQLITE_BUSY ){
51377 rc = SQLITE_OK;
51378 }
51379
51380 if( rc==SQLITE_OK ){
51381 /* Take a SHARED lock on the DMS byte. */
51382 rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
51383 if( rc==SQLITE_OK ){
51384 pShmNode->isUnlocked = 0;
51385 }
51386 }
51387
51388 return rc;
51389 }
51390
51391
51392 /*
51393 ** Convert a UTF-8 filename into whatever form the underlying
51394 ** operating system wants filenames in. Space to hold the result
51395 ** is obtained from malloc and must be freed by the calling
@@ -51483,10 +51612,212 @@
51483 }
51484 #endif
51485 /* caller will handle out of memory */
51486 return zConverted;
51487 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51488
51489 /*
51490 ** This function is used to open a handle on a *-shm file.
51491 **
51492 ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
@@ -51579,10 +51910,64 @@
51579 *pbReadonly = bReadonly;
51580 *ph = h;
51581 return rc;
51582 }
51583
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51584
51585 /*
51586 ** Open the shared-memory area associated with database file pDbFd.
51587 */
51588 static int winOpenSharedMemory(winFile *pDbFd){
@@ -51605,19 +51990,14 @@
51605 return SQLITE_IOERR_NOMEM_BKPT;
51606 }
51607 pNew->zFilename = (char*)&pNew[1];
51608 pNew->hSharedShm = INVALID_HANDLE_VALUE;
51609 pNew->isUnlocked = 1;
 
51610 sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
51611 sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
51612
51613 /* Open a file-handle on the *-shm file for this connection. This file-handle
51614 ** is only used for locking. The mapping of the *-shm file is created using
51615 ** the shared file handle in winShmNode.hSharedShm. */
51616 p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
51617 rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm);
51618
51619 /* Look to see if there is an existing winShmNode that can be used.
51620 ** If no matching winShmNode currently exists, then create a new one. */
51621 winShmEnterMutex();
51622 for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
51623 /* TBD need to come up with better match here. Perhaps
@@ -51634,11 +52014,11 @@
51634 }
51635
51636 /* Open a file-handle to use for mappings, and for the DMS lock. */
51637 if( rc==SQLITE_OK ){
51638 HANDLE h = INVALID_HANDLE_VALUE;
51639 pShmNode->isReadonly = p->bReadonly;
51640 rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
51641 pShmNode->hSharedShm = h;
51642 }
51643
51644 /* If successful, link the new winShmNode into the global list. If an
@@ -51656,24 +52036,39 @@
51656 }
51657
51658 /* If no error has occurred, link the winShm object to the winShmNode and
51659 ** the winShm to pDbFd. */
51660 if( rc==SQLITE_OK ){
 
51661 p->pShmNode = pShmNode;
51662 pShmNode->nRef++;
 
51663 #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51664 p->id = pShmNode->nextShmId++;
51665 #endif
51666 pDbFd->pShm = p;
 
51667 }else if( p ){
51668 winHandleClose(p->hShm);
51669 sqlite3_free(p);
51670 }
51671
51672 assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
51673 winShmLeaveMutex();
51674 sqlite3_free(pNew);
 
 
 
 
 
 
 
 
 
 
 
 
 
51675 return rc;
51676 }
51677
51678 /*
51679 ** Close a connection to shared-memory. Delete the underlying
@@ -51681,37 +52076,11 @@
51681 */
51682 static int winShmUnmap(
51683 sqlite3_file *fd, /* Database holding shared memory */
51684 int deleteFlag /* Delete after closing if true */
51685 ){
51686 winFile *pDbFd; /* Database holding shared-memory */
51687 winShm *p; /* The connection to be closed */
51688 winShmNode *pShmNode; /* The underlying shared-memory file */
51689
51690 pDbFd = (winFile*)fd;
51691 p = pDbFd->pShm;
51692 if( p==0 ) return SQLITE_OK;
51693 if( p->hShm!=INVALID_HANDLE_VALUE ){
51694 osCloseHandle(p->hShm);
51695 }
51696
51697 pShmNode = p->pShmNode;
51698 winShmEnterMutex();
51699
51700 /* If pShmNode->nRef has reached 0, then close the underlying
51701 ** shared-memory file, too. */
51702 assert( pShmNode->nRef>0 );
51703 pShmNode->nRef--;
51704 if( pShmNode->nRef==0 ){
51705 winShmPurge(pDbFd->pVfs, deleteFlag);
51706 }
51707 winShmLeaveMutex();
51708
51709 /* Free the connection p */
51710 sqlite3_free(p);
51711 pDbFd->pShm = 0;
51712 return SQLITE_OK;
51713 }
51714
51715 /*
51716 ** Change the lock state for a shared-memory segment.
51717 */
@@ -51776,29 +52145,75 @@
51776 );
51777 if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
51778 || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
51779 || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
51780 ){
 
51781
51782 if( flags & SQLITE_SHM_UNLOCK ){
51783 /* Case (a) - unlock. */
51784
51785 assert( (p->exclMask & p->sharedMask)==0 );
51786 assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
51787 assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
51788
51789 rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51790
51791 /* If successful, also clear the bits in sharedMask/exclMask */
51792 if( rc==SQLITE_OK ){
51793 p->exclMask = (p->exclMask & ~mask);
51794 p->sharedMask = (p->sharedMask & ~mask);
51795 }
51796 }else{
51797 int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
51798 DWORD nMs = winFileBusyTimeout(pDbFd);
51799 rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51800 if( rc==SQLITE_OK ){
51801 if( bExcl ){
51802 p->exclMask = (p->exclMask | mask);
51803 }else{
51804 p->sharedMask = (p->sharedMask | mask);
@@ -61825,18 +62240,31 @@
61825 SQLITE_PRIVATE void sqlite3PagerSetFlags(
61826 Pager *pPager, /* The pager to set safety level for */
61827 unsigned pgFlags /* Various flags */
61828 ){
61829 unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
61830 if( pPager->tempFile ){
61831 pPager->noSync = 1;
61832 pPager->fullSync = 0;
61833 pPager->extraSync = 0;
61834 }else{
61835 pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0;
61836 pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
61837 pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0;
 
 
 
 
 
 
 
 
 
 
 
 
 
61838 }
61839 if( pPager->noSync ){
61840 pPager->syncFlags = 0;
61841 }else if( pgFlags & PAGER_FULLFSYNC ){
61842 pPager->syncFlags = SQLITE_SYNC_FULL;
@@ -65725,11 +66153,11 @@
65725 */
65726 sqlite3_exec(db, "PRAGMA table_list",0,0,0);
65727 }
65728 if( pPager->pWal ){
65729 rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
65730 (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
65731 pPager->pBusyHandlerArg,
65732 pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
65733 pnLog, pnCkpt
65734 );
65735 }
@@ -70330,11 +70758,12 @@
70330 assert( pWal->ckptLock==0 );
70331 assert( pWal->writeLock==0 );
70332
70333 /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
70334 ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
70335 assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
 
70336
70337 if( pWal->readOnly ) return SQLITE_READONLY;
70338 WALTRACE(("WAL%p: checkpoint begins\n", pWal));
70339
70340 /* Enable blocking locks, if possible. */
@@ -70347,35 +70776,39 @@
70347 ** checkpoint operation at the same time, the lock cannot be obtained and
70348 ** SQLITE_BUSY is returned.
70349 ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
70350 ** it will not be invoked in this case.
70351 */
70352 rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
70353 testcase( rc==SQLITE_BUSY );
70354 testcase( rc!=SQLITE_OK && xBusy2!=0 );
70355 if( rc==SQLITE_OK ){
70356 pWal->ckptLock = 1;
70357
70358 /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
70359 ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
70360 ** file.
70361 **
70362 ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
70363 ** immediately, and a busy-handler is configured, it is invoked and the
70364 ** writer lock retried until either the busy-handler returns 0 or the
70365 ** lock is successfully obtained.
70366 */
70367 if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
70368 rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
70369 if( rc==SQLITE_OK ){
70370 pWal->writeLock = 1;
70371 }else if( rc==SQLITE_BUSY ){
70372 eMode2 = SQLITE_CHECKPOINT_PASSIVE;
70373 xBusy2 = 0;
70374 rc = SQLITE_OK;
70375 }
70376 }
 
 
 
 
70377 }
70378
70379
70380 /* Read the wal-index header. */
70381 SEH_TRY {
@@ -70385,21 +70818,21 @@
70385 ** or invoke the busy handler. The only lock such a checkpoint may
70386 ** attempt to obtain is a lock on a read-slot, and it should give up
70387 ** immediately and do a partial checkpoint if it cannot obtain it. */
70388 walDisableBlocking(pWal);
70389 rc = walIndexReadHdr(pWal, &isChanged);
70390 if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
70391 if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
70392 sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
70393 }
70394 }
70395
70396 /* Copy data from the log to the database file. */
70397 if( rc==SQLITE_OK ){
70398 if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
70399 rc = SQLITE_CORRUPT_BKPT;
70400 }else{
70401 rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
70402 }
70403
70404 /* If no error occurred, set the output variables. */
70405 if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
@@ -89078,14 +89511,16 @@
89078 ** simple case then too.
89079 */
89080 if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
89081 || nTrans<=1
89082 ){
89083 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89084 Btree *pBt = db->aDb[i].pBt;
89085 if( pBt ){
89086 rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
 
 
89087 }
89088 }
89089
89090 /* Do the commit only if all databases successfully complete phase 1.
89091 ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
@@ -89092,11 +89527,13 @@
89092 ** IO error while deleting or truncating a journal file. It is unlikely,
89093 ** but could happen. In this case abandon processing and return the error.
89094 */
89095 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89096 Btree *pBt = db->aDb[i].pBt;
89097 if( pBt ){
 
 
89098 rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
89099 }
89100 }
89101 if( rc==SQLITE_OK ){
89102 sqlite3VtabCommit(db);
@@ -89347,32 +89784,35 @@
89347 return SQLITE_OK;
89348 }
89349
89350
89351 /*
89352 ** This function is called when a transaction opened by the database
89353 ** handle associated with the VM passed as an argument is about to be
89354 ** committed. If there are outstanding deferred foreign key constraint
89355 ** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
89356 **
89357 ** If there are outstanding FK violations and this function returns
89358 ** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89359 ** and write an error message to it. Then return SQLITE_ERROR.
89360 */
89361 #ifndef SQLITE_OMIT_FOREIGN_KEY
89362 SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
 
 
 
 
 
 
 
 
 
 
 
89363 sqlite3 *db = p->db;
89364 if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
89365 || (!deferred && p->nFkConstraint>0)
89366 ){
89367 p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89368 p->errorAction = OE_Abort;
89369 sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89370 if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89371 return SQLITE_CONSTRAINT_FOREIGNKEY;
89372 }
89373 return SQLITE_OK;
89374 }
89375 #endif
89376
89377 /*
89378 ** This routine is called the when a VDBE tries to halt. If the VDBE
@@ -89462,11 +89902,11 @@
89462 }
89463 }
89464
89465 /* Check for immediate foreign key violations. */
89466 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89467 (void)sqlite3VdbeCheckFk(p, 0);
89468 }
89469
89470 /* If the auto-commit flag is set and this is the only active writer
89471 ** VM, then we do either a commit or rollback of the current transaction.
89472 **
@@ -89476,11 +89916,11 @@
89476 if( !sqlite3VtabInSync(db)
89477 && db->autoCommit
89478 && db->nVdbeWrite==(p->readOnly==0)
89479 ){
89480 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89481 rc = sqlite3VdbeCheckFk(p, 1);
89482 if( rc!=SQLITE_OK ){
89483 if( NEVER(p->readOnly) ){
89484 sqlite3VdbeLeave(p);
89485 return SQLITE_ERROR;
89486 }
@@ -90341,19 +90781,19 @@
90341 /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
90342 pMem->szMalloc = 0;
90343 pMem->z = 0;
90344 sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
90345 d += sqlite3VdbeSerialTypeLen(serial_type);
90346 pMem++;
90347 if( (++u)>=p->nField ) break;
 
90348 }
90349 if( d>(u32)nKey && u ){
90350 assert( CORRUPT_DB );
90351 /* In a corrupt record entry, the last pMem might have been set up using
90352 ** uninitialized memory. Overwrite its value with NULL, to prevent
90353 ** warnings from MSAN. */
90354 sqlite3VdbeMemSetNull(pMem-1);
90355 }
90356 testcase( u == pKeyInfo->nKeyField + 1 );
90357 testcase( u < pKeyInfo->nKeyField + 1 );
90358 assert( u<=pKeyInfo->nKeyField + 1 );
90359 p->nField = u;
@@ -90520,10 +90960,36 @@
90520 ** Both *pMem1 and *pMem2 contain string values. Compare the two values
90521 ** using the collation sequence pColl. As usual, return a negative , zero
90522 ** or positive value if *pMem1 is less than, equal to or greater than
90523 ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
90524 */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90525 static int vdbeCompareMemString(
90526 const Mem *pMem1,
90527 const Mem *pMem2,
90528 const CollSeq *pColl,
90529 u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
@@ -90531,29 +90997,11 @@
90531 if( pMem1->enc==pColl->enc ){
90532 /* The strings are already in the correct encoding. Call the
90533 ** comparison function directly */
90534 return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
90535 }else{
90536 int rc;
90537 const void *v1, *v2;
90538 Mem c1;
90539 Mem c2;
90540 sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90541 sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90542 sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90543 sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90544 v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90545 v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90546 if( (v1==0 || v2==0) ){
90547 if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90548 rc = 0;
90549 }else{
90550 rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90551 }
90552 sqlite3VdbeMemReleaseMalloc(&c1);
90553 sqlite3VdbeMemReleaseMalloc(&c2);
90554 return rc;
90555 }
90556 }
90557
90558 /*
90559 ** The input pBlob is guaranteed to be a Blob that is not marked
@@ -91607,11 +92055,11 @@
91607
91608 preupdate.v = v;
91609 preupdate.pCsr = pCsr;
91610 preupdate.op = op;
91611 preupdate.iNewReg = iReg;
91612 preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
91613 preupdate.pKeyinfo->db = db;
91614 preupdate.pKeyinfo->enc = ENC(db);
91615 preupdate.pKeyinfo->nKeyField = pTab->nCol;
91616 preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
91617 preupdate.iKey1 = iKey1;
@@ -91641,10 +92089,21 @@
91641 sqlite3DbFree(db, preupdate.apDflt);
91642 }
91643 }
91644 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
91645
 
 
 
 
 
 
 
 
 
 
 
91646 /************** End of vdbeaux.c *********************************************/
91647 /************** Begin file vdbeapi.c *****************************************/
91648 /*
91649 ** 2004 May 26
91650 **
@@ -93338,12 +93797,16 @@
93338 if( rc==SQLITE_OK ){
93339 assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
93340 if( zData!=0 ){
93341 pVar = &p->aVar[i-1];
93342 rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
93343 if( rc==SQLITE_OK && encoding!=0 ){
93344 rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
 
 
 
 
93345 }
93346 if( rc ){
93347 sqlite3Error(p->db, rc);
93348 rc = sqlite3ApiExit(p->db, rc);
93349 }
@@ -95247,11 +95710,11 @@
95247 ** to run faster.
95248 */
95249 static SQLITE_NOINLINE int vdbeColumnFromOverflow(
95250 VdbeCursor *pC, /* The BTree cursor from which we are reading */
95251 int iCol, /* The column to read */
95252 int t, /* The serial-type code for the column value */
95253 i64 iOffset, /* Offset to the start of the content value */
95254 u32 cacheStatus, /* Current Vdbe.cacheCtr value */
95255 u32 colCacheCtr, /* Current value of the column cache counter */
95256 Mem *pDest /* Store the value into this register. */
95257 ){
@@ -96256,11 +96719,11 @@
96256 ** exits. This opcode is used to raise foreign key constraint errors prior
96257 ** to returning results such as a row change count or the result of a
96258 ** RETURNING clause.
96259 */
96260 case OP_FkCheck: {
96261 if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
96262 goto abort_due_to_error;
96263 }
96264 break;
96265 }
96266
@@ -96348,11 +96811,12 @@
96348 flags2 = pIn2->flags & ~MEM_Str;
96349 }else if( (flags2 & MEM_Zero)!=0 ){
96350 if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
96351 flags2 = pIn2->flags & ~MEM_Str;
96352 }
96353 nByte = pIn1->n + pIn2->n;
 
96354 if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
96355 goto too_big;
96356 }
96357 if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
96358 goto no_mem;
@@ -98173,11 +98637,11 @@
98173 assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
98174 assert( pRec->n>=0 );
98175 len = (u32)pRec->n;
98176 serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
98177 if( pRec->flags & MEM_Zero ){
98178 serial_type += pRec->u.nZero*2;
98179 if( nData ){
98180 if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
98181 len += pRec->u.nZero;
98182 }else{
98183 nZero += pRec->u.nZero;
@@ -98440,11 +98904,11 @@
98440 ** and this is a RELEASE command, then the current transaction
98441 ** is committed.
98442 */
98443 int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
98444 if( isTransaction && p1==SAVEPOINT_RELEASE ){
98445 if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98446 goto vdbe_return;
98447 }
98448 db->autoCommit = 1;
98449 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
98450 p->pc = (int)(pOp - aOp);
@@ -98558,11 +99022,11 @@
98558 */
98559 sqlite3VdbeError(p, "cannot commit transaction - "
98560 "SQL statements in progress");
98561 rc = SQLITE_BUSY;
98562 goto abort_due_to_error;
98563 }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98564 goto vdbe_return;
98565 }else{
98566 db->autoCommit = (u8)desiredAutoCommit;
98567 }
98568 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
@@ -102490,10 +102954,11 @@
102490 aRes[1] = aRes[2] = -1;
102491 assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
102492 || pOp->p2==SQLITE_CHECKPOINT_FULL
102493 || pOp->p2==SQLITE_CHECKPOINT_RESTART
102494 || pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
 
102495 );
102496 rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
102497 if( rc ){
102498 if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
102499 rc = SQLITE_OK;
@@ -104689,10 +105154,11 @@
104689 UnpackedRecord *pUnpacked; /* Space to unpack a record */
104690 SorterList list; /* List for thread to write to a PMA */
104691 SorterCompare xCompare; /* Compare function to use */
104692 SorterFile file; /* Temp file for level-0 PMAs */
104693 SorterFile file2; /* Space for other PMAs */
 
104694 };
104695
104696
104697 /*
104698 ** Main sorter structure. A single instance of this is allocated for each
@@ -104809,10 +105275,11 @@
104809 int nBuffer; /* Size of write buffer in bytes */
104810 int iBufStart; /* First byte of buffer to write */
104811 int iBufEnd; /* Last byte of buffer to write */
104812 i64 iWriteOff; /* Offset of start of buffer in file */
104813 sqlite3_file *pFd; /* File handle to write to */
 
104814 };
104815
104816 /*
104817 ** This object is the header on a single record while that record is being
104818 ** held in memory and prior to being written out as part of a PMA.
@@ -105667,10 +106134,16 @@
105667 SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
105668 VdbeSorter *pSorter;
105669 assert( pCsr->eCurType==CURTYPE_SORTER );
105670 pSorter = pCsr->uc.pSorter;
105671 if( pSorter ){
 
 
 
 
 
 
105672 sqlite3VdbeSorterReset(db, pSorter);
105673 sqlite3_free(pSorter->list.aMemory);
105674 sqlite3DbFree(db, pSorter);
105675 pCsr->uc.pSorter = 0;
105676 }
@@ -105892,10 +106365,11 @@
105892 if( p->iBufEnd==p->nBuffer ){
105893 p->eFWErr = sqlite3OsWrite(p->pFd,
105894 &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
105895 p->iWriteOff + p->iBufStart
105896 );
 
105897 p->iBufStart = p->iBufEnd = 0;
105898 p->iWriteOff += p->nBuffer;
105899 }
105900 assert( p->iBufEnd<p->nBuffer );
105901
@@ -105908,21 +106382,24 @@
105908 ** The results of using the PMA-writer after this call are undefined.
105909 ** Return SQLITE_OK if flushing the buffered data succeeds or is not
105910 ** required. Otherwise, return an SQLite error code.
105911 **
105912 ** Before returning, set *piEof to the offset immediately following the
105913 ** last byte written to the file.
 
105914 */
105915 static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){
105916 int rc;
105917 if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
105918 p->eFWErr = sqlite3OsWrite(p->pFd,
105919 &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
105920 p->iWriteOff + p->iBufStart
105921 );
 
105922 }
105923 *piEof = (p->iWriteOff + p->iBufEnd);
 
105924 sqlite3_free(p->aBuffer);
105925 rc = p->eFWErr;
105926 memset(p, 0, sizeof(PmaWriter));
105927 return rc;
105928 }
@@ -105998,11 +106475,11 @@
105998 vdbePmaWriteVarint(&writer, p->nVal);
105999 vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal);
106000 if( pList->aMemory==0 ) sqlite3_free(p);
106001 }
106002 pList->pList = p;
106003 rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof);
106004 }
106005
106006 vdbeSorterWorkDebug(pTask, "exit");
106007 assert( rc!=SQLITE_OK || pList->pList==0 );
106008 assert( rc!=SQLITE_OK || pTask->file.iEof==iSz );
@@ -106312,11 +106789,11 @@
106312 vdbePmaWriteBlob(&writer, pReader->aKey, nKey);
106313 assert( pIncr->pMerger->pTask==pTask );
106314 rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy);
106315 }
106316
106317 rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof);
106318 if( rc==SQLITE_OK ) rc = rc2;
106319 vdbeSorterPopulateDebug(pTask, "exit");
106320 return rc;
106321 }
106322
@@ -109256,12 +109733,12 @@
109256 assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
109257 if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
109258
109259 /*
109260 ** Expression p should encode a floating point value between 1.0 and 0.0.
109261 ** Return 1024 times this value. Or return -1 if p is not a floating point
109262 ** value between 1.0 and 0.0.
109263 */
109264 static int exprProbability(Expr *p){
109265 double r = -1.0;
109266 if( p->op!=TK_FLOAT ) return -1;
109267 assert( !ExprHasProperty(p, EP_IntValue) );
@@ -110613,18 +111090,21 @@
110613 ExprList *pList /* Expression list to resolve. May be NULL. */
110614 ){
110615 SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
110616 NameContext sNC; /* Name context for pParse->pNewTable */
110617 int rc;
110618 u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
 
 
 
110619
110620 assert( type==0 || pTab!=0 );
110621 assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
110622 || type==NC_GenCol || pTab==0 );
110623 memset(&sNC, 0, sizeof(sNC));
110624 pSrc = (SrcList*)srcSpace;
110625 memset(pSrc, 0, SZ_SRCLIST_1);
110626 if( pTab ){
110627 pSrc->nSrc = 1;
110628 pSrc->a[0].zName = pTab->zName;
110629 pSrc->a[0].pSTab = pTab;
110630 pSrc->a[0].iCursor = -1;
@@ -111883,10 +112363,15 @@
111883 if( IsWindowFunc(pExpr) ){
111884 sqlite3ExprOrderByAggregateError(pParse, pExpr);
111885 sqlite3ExprListDelete(db, pOrderBy);
111886 return;
111887 }
 
 
 
 
 
111888
111889 pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
111890 if( pOB==0 ){
111891 sqlite3ExprListDelete(db, pOrderBy);
111892 return;
@@ -113079,13 +113564,12 @@
113079 }
113080 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
113081 if( addrIsNull==0 ){
113082 /*
113083 ** If the right operand contains a subquery and the left operand does not
113084 ** and the left operand might be NULL, then check the left operand do
113085 ** an IsNull check on the left operand before computing the right
113086 ** operand.
113087 */
113088 if( ExprHasProperty(pExpr->pRight, EP_Subquery)
113089 && sqlite3ExprCanBeNull(pExpr->pLeft)
113090 ){
113091 addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r1);
@@ -114645,11 +115129,10 @@
114645 int destIfNull /* Jump here if the results are unknown due to NULLs */
114646 ){
114647 int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
114648 int eType; /* Type of the RHS */
114649 int rLhs; /* Register(s) holding the LHS values */
114650 int rLhsOrig; /* LHS values prior to reordering by aiMap[] */
114651 Vdbe *v; /* Statement under construction */
114652 int *aiMap = 0; /* Map from vector field to index column */
114653 char *zAff = 0; /* Affinity string for comparisons */
114654 int nVector; /* Size of vectors for this IN operator */
114655 int iDummy; /* Dummy parameter to exprCodeVector() */
@@ -114708,23 +115191,12 @@
114708 ** Avoid factoring the LHS of the IN(...) expression out of the loop,
114709 ** even if it is constant, as OP_Affinity may be used on the register
114710 ** by code generated below. */
114711 assert( pParse->okConstFactor==okConstFactor );
114712 pParse->okConstFactor = 0;
114713 rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
114714 pParse->okConstFactor = okConstFactor;
114715 for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
114716 if( i==nVector ){
114717 /* LHS fields are not reordered */
114718 rLhs = rLhsOrig;
114719 }else{
114720 /* Need to reorder the LHS fields according to aiMap */
114721 rLhs = sqlite3GetTempRange(pParse, nVector);
114722 for(i=0; i<nVector; i++){
114723 sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
114724 }
114725 }
114726
114727 /* If sqlite3FindInIndex() did not find or create an index that is
114728 ** suitable for evaluating the IN operator, then evaluate using a
114729 ** sequence of comparisons.
114730 **
@@ -114735,10 +115207,11 @@
114735 CollSeq *pColl;
114736 int labelOk = sqlite3VdbeMakeLabel(pParse);
114737 int r2, regToFree;
114738 int regCkNull = 0;
114739 int ii;
 
114740 assert( ExprUseXList(pExpr) );
114741 pList = pExpr->x.pList;
114742 pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
114743 if( destIfNull!=destIfFalse ){
114744 regCkNull = sqlite3GetTempReg(pParse);
@@ -114775,10 +115248,30 @@
114775 }
114776 sqlite3VdbeResolveLabel(v, labelOk);
114777 sqlite3ReleaseTempReg(pParse, regCkNull);
114778 goto sqlite3ExprCodeIN_finished;
114779 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114780
114781 /* Step 2: Check to see if the LHS contains any NULL columns. If the
114782 ** LHS does contain NULLs then the result must be either FALSE or NULL.
114783 ** We will then skip the binary search of the RHS.
114784 */
@@ -114802,15 +115295,15 @@
114802 */
114803 if( eType==IN_INDEX_ROWID ){
114804 /* In this case, the RHS is the ROWID of table b-tree and so we also
114805 ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
114806 ** into a single opcode. */
 
114807 sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs);
114808 VdbeCoverage(v);
114809 addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
114810 }else{
114811 sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
114812 if( destIfFalse==destIfNull ){
114813 /* Combine Step 3 and Step 5 into a single opcode */
114814 if( ExprHasProperty(pExpr, EP_Subrtn) ){
114815 const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
114816 assert( pOp->opcode==OP_Once || pParse->nErr );
@@ -114884,11 +115377,10 @@
114884
114885 /* Jumps here in order to return true. */
114886 sqlite3VdbeJumpHere(v, addrTruthOp);
114887
114888 sqlite3ExprCodeIN_finished:
114889 if( rLhs!=rLhsOrig ) sqlite3ReleaseTempReg(pParse, rLhs);
114890 VdbeComment((v, "end IN expr"));
114891 sqlite3ExprCodeIN_oom_error:
114892 sqlite3DbFree(pParse->db, aiMap);
114893 sqlite3DbFree(pParse->db, zAff);
114894 }
@@ -115442,10 +115934,84 @@
115442 }
115443 }
115444 return 0;
115445 }
115446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115447
115448 /*
115449 ** Generate code into the current Vdbe to evaluate the given
115450 ** expression. Attempt to store the results in register "target".
115451 ** Return the register where results are stored.
@@ -115633,10 +116199,16 @@
115633 case TK_STRING: {
115634 assert( !ExprHasProperty(pExpr, EP_IntValue) );
115635 sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
115636 return target;
115637 }
 
 
 
 
 
 
115638 default: {
115639 /* Make NULL the default case so that if a bug causes an illegal
115640 ** Expr node to be passed into this function, it will be handled
115641 ** sanely and not crash. But keep the assert() to bring the problem
115642 ** to the attention of the developers. */
@@ -115724,16 +116296,18 @@
115724 sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
115725 }
115726 }
115727 testcase( regFree1==0 );
115728 testcase( regFree2==0 );
115729
115730 }
115731 break;
115732 }
115733 case TK_AND:
115734 case TK_OR:
 
 
 
115735 case TK_PLUS:
115736 case TK_STAR:
115737 case TK_MINUS:
115738 case TK_REM:
115739 case TK_BITAND:
@@ -115741,12 +116315,10 @@
115741 case TK_SLASH:
115742 case TK_LSHIFT:
115743 case TK_RSHIFT:
115744 case TK_CONCAT: {
115745 int addrIsNull;
115746 assert( TK_AND==OP_And ); testcase( op==TK_AND );
115747 assert( TK_OR==OP_Or ); testcase( op==TK_OR );
115748 assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
115749 assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS );
115750 assert( TK_REM==OP_Remainder ); testcase( op==TK_REM );
115751 assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND );
115752 assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR );
@@ -116341,10 +116913,29 @@
116341 }
116342 pParse->pConstExpr = p;
116343 }
116344 return regDest;
116345 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116346
116347 /*
116348 ** Generate code to evaluate an expression and store the results
116349 ** into a register. Return the register number where the results
116350 ** are stored.
@@ -123872,10 +124463,20 @@
123872 if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
123873 Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
123874 if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
123875 pMod = sqlite3PragmaVtabRegister(db, zName);
123876 }
 
 
 
 
 
 
 
 
 
 
123877 if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
123878 testcase( pMod->pEpoTab==0 );
123879 return pMod->pEpoTab;
123880 }
123881 }
@@ -124510,11 +125111,11 @@
124510 */
124511 SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
124512 int i;
124513 i16 iCol16;
124514 assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
124515 assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 );
124516 iCol16 = iCol;
124517 for(i=0; i<pIdx->nColumn; i++){
124518 if( iCol16==pIdx->aiColumn[i] ){
124519 return i;
124520 }
@@ -124807,10 +125408,13 @@
124807 sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
124808 sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
124809 sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
124810 sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
124811 sqlite3VdbeAddOp0(v, OP_Close);
 
 
 
124812 }
124813
124814 /* Normal (non-error) return. */
124815 return;
124816
@@ -129102,18 +129706,23 @@
129102 pKey->aSortFlags[i] = pIdx->aSortOrder[i];
129103 assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
129104 }
129105 if( pParse->nErr ){
129106 assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
129107 if( pIdx->bNoQuery==0 ){
 
 
129108 /* Deactivate the index because it contains an unknown collating
129109 ** sequence. The only way to reactive the index is to reload the
129110 ** schema. Adding the missing collating sequence later does not
129111 ** reactive the index. The application had the chance to register
129112 ** the missing index using the collation-needed callback. For
129113 ** simplicity, SQLite will not give the application a second chance.
129114 */
 
 
 
129115 pIdx->bNoQuery = 1;
129116 pParse->rc = SQLITE_ERROR_RETRY;
129117 }
129118 sqlite3KeyInfoUnref(pKey);
129119 pKey = 0;
@@ -131977,11 +132586,11 @@
131977 ** a decoding of those digits into *pVal. Or return false if any
131978 ** one of the first N characters in z[] is not a hexadecimal digit.
131979 */
131980 static int isNHex(const char *z, int N, u32 *pVal){
131981 int i;
131982 int v = 0;
131983 for(i=0; i<N; i++){
131984 if( !sqlite3Isxdigit(z[i]) ) return 0;
131985 v = (v<<4) + sqlite3HexToInt(z[i]);
131986 }
131987 *pVal = v;
@@ -132490,10 +133099,11 @@
132490 int nSep,
132491 const char *zSep
132492 ){
132493 i64 j, n = 0;
132494 int i;
 
132495 char *z;
132496 for(i=0; i<argc; i++){
132497 n += sqlite3_value_bytes(argv[i]);
132498 }
132499 n += (argc-1)*(i64)nSep;
@@ -132506,16 +133116,17 @@
132506 for(i=0; i<argc; i++){
132507 if( sqlite3_value_type(argv[i])!=SQLITE_NULL ){
132508 int k = sqlite3_value_bytes(argv[i]);
132509 const char *v = (const char*)sqlite3_value_text(argv[i]);
132510 if( v!=0 ){
132511 if( j>0 && nSep>0 ){
132512 memcpy(&z[j], zSep, nSep);
132513 j += nSep;
132514 }
132515 memcpy(&z[j], v, k);
132516 j += k;
 
132517 }
132518 }
132519 }
132520 z[j] = 0;
132521 assert( j<=n );
@@ -133456,10 +134067,456 @@
133456 type0 = sqlite3_value_numeric_type(argv[0]);
133457 if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
133458 x = sqlite3_value_double(argv[0]);
133459 sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
133460 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133461
133462 #ifdef SQLITE_DEBUG
133463 /*
133464 ** Implementation of fpdecode(x,y,z) function.
133465 **
@@ -133687,10 +134744,25 @@
133687 WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
133688 groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
133689 WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
133690 groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
133691
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133692 LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
133693 #ifdef SQLITE_CASE_SENSITIVE_LIKE
133694 LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
133695 LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
133696 #else
@@ -139184,10 +140256,14 @@
139184 /* Version 3.44.0 and later */
139185 void *(*get_clientdata)(sqlite3*,const char*);
139186 int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
139187 /* Version 3.50.0 and later */
139188 int (*setlk_timeout)(sqlite3*,int,int);
 
 
 
 
139189 };
139190
139191 /*
139192 ** This is the function signature used for all extension entry points. It
139193 ** is also defined in the file "loadext.c".
@@ -139519,10 +140595,13 @@
139519 /* Version 3.44.0 and later */
139520 #define sqlite3_get_clientdata sqlite3_api->get_clientdata
139521 #define sqlite3_set_clientdata sqlite3_api->set_clientdata
139522 /* Version 3.50.0 and later */
139523 #define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
 
 
 
139524 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
139525
139526 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
139527 /* This case when the file really is being compiled as a loadable
139528 ** extension */
@@ -140042,11 +141121,14 @@
140042 sqlite3_stmt_explain,
140043 /* Version 3.44.0 and later */
140044 sqlite3_get_clientdata,
140045 sqlite3_set_clientdata,
140046 /* Version 3.50.0 and later */
140047 sqlite3_setlk_timeout
 
 
 
140048 };
140049
140050 /* True if x is the directory separator character
140051 */
140052 #if SQLITE_OS_WIN
@@ -141503,10 +142585,26 @@
141503 addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1);
141504 VdbeCoverage(v);
141505 sqlite3VdbeAddOp0(v, OP_Halt);
141506 return addr;
141507 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141508
141509 /*
141510 ** Process a pragma statement.
141511 **
141512 ** Pragmas are of this form:
@@ -142849,11 +143947,11 @@
142849 pTbls = &db->aDb[i].pSchema->tblHash;
142850 for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
142851 Table *pTab = sqliteHashData(x); /* Current table */
142852 Index *pIdx; /* An index on pTab */
142853 int nIdx; /* Number of indexes on pTab */
142854 if( pObjTab && pObjTab!=pTab ) continue;
142855 if( HasRowid(pTab) ) cnt++;
142856 for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
142857 }
142858 if( cnt==0 ) continue;
142859 if( pObjTab ) cnt++;
@@ -142862,11 +143960,11 @@
142862 cnt = 0;
142863 if( pObjTab ) aRoot[++cnt] = 0;
142864 for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
142865 Table *pTab = sqliteHashData(x);
142866 Index *pIdx;
142867 if( pObjTab && pObjTab!=pTab ) continue;
142868 if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
142869 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
142870 aRoot[++cnt] = pIdx->tnum;
142871 }
142872 }
@@ -142893,11 +143991,11 @@
142893 sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
142894 for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
142895 int iTab = 0;
142896 Table *pTab = sqliteHashData(x);
142897 Index *pIdx;
142898 if( pObjTab && pObjTab!=pTab ) continue;
142899 if( HasRowid(pTab) ){
142900 iTab = cnt++;
142901 }else{
142902 iTab = cnt;
142903 for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){
@@ -142929,11 +144027,11 @@
142929 int r1 = -1;
142930 int bStrict; /* True for a STRICT table */
142931 int r2; /* Previous key for WITHOUT ROWID tables */
142932 int mxCol; /* Maximum non-virtual column number */
142933
142934 if( pObjTab && pObjTab!=pTab ) continue;
142935 if( !IsOrdinaryTable(pTab) ) continue;
142936 if( isQuick || HasRowid(pTab) ){
142937 pPk = 0;
142938 r2 = 0;
142939 }else{
@@ -143253,11 +144351,11 @@
143253 */
143254 for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
143255 Table *pTab = sqliteHashData(x);
143256 sqlite3_vtab *pVTab;
143257 int a1;
143258 if( pObjTab && pObjTab!=pTab ) continue;
143259 if( IsOrdinaryTable(pTab) ) continue;
143260 if( !IsVirtual(pTab) ) continue;
143261 if( pTab->nCol<=0 ){
143262 const char *zMod = pTab->u.vtab.azArg[0];
143263 if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
@@ -143485,10 +144583,12 @@
143485 eMode = SQLITE_CHECKPOINT_FULL;
143486 }else if( sqlite3StrICmp(zRight, "restart")==0 ){
143487 eMode = SQLITE_CHECKPOINT_RESTART;
143488 }else if( sqlite3StrICmp(zRight, "truncate")==0 ){
143489 eMode = SQLITE_CHECKPOINT_TRUNCATE;
 
 
143490 }
143491 }
143492 pParse->nMem = 3;
143493 sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
143494 sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
@@ -145051,13 +146151,15 @@
145051 ** or encounters a permanent error. A schema problem after one schema
145052 ** reset is considered a permanent error. */
145053 rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
145054 assert( rc==SQLITE_OK || *ppStmt==0 );
145055 if( rc==SQLITE_OK || db->mallocFailed ) break;
145056 }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
145057 || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
 
145058 sqlite3BtreeLeaveAll(db);
 
145059 rc = sqlite3ApiExit(db, rc);
145060 assert( (rc&db->errMask)==rc );
145061 db->busyHandler.nBusy = 0;
145062 sqlite3_mutex_leave(db->mutex);
145063 assert( rc==SQLITE_OK || (*ppStmt)==0 );
@@ -145727,12 +146829,11 @@
145727 while( p ){
145728 ExprSetProperty(p, joinFlag);
145729 assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
145730 ExprSetVVAProperty(p, EP_NoReduce);
145731 p->w.iJoin = iTable;
145732 if( p->op==TK_FUNCTION ){
145733 assert( ExprUseXList(p) );
145734 if( p->x.pList ){
145735 int i;
145736 for(i=0; i<p->x.pList->nExpr; i++){
145737 sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
145738 }
@@ -145944,10 +147045,11 @@
145944 else if( pRight->u3.pOn ){
145945 sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
145946 p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
145947 pRight->u3.pOn = 0;
145948 pRight->fg.isOn = 1;
 
145949 }
145950 }
145951 return 0;
145952 }
145953
@@ -146830,11 +147932,14 @@
146830 ** Allocate a KeyInfo object sufficient for an index of N key columns and
146831 ** X extra columns.
146832 */
146833 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
146834 int nExtra = (N+X)*(sizeof(CollSeq*)+1);
146835 KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
 
 
 
146836 if( p ){
146837 p->aSortFlags = (u8*)&p->aColl[N+X];
146838 p->nKeyField = (u16)N;
146839 p->nAllField = (u16)(N+X);
146840 p->enc = ENC(db);
@@ -149149,11 +150254,11 @@
149149 ** expressions in pEList.
149150 **
149151 ** ## About "isOuterJoin":
149152 **
149153 ** The isOuterJoin column indicates that the replacement will occur into a
149154 ** position in the parent that NULL-able due to an OUTER JOIN. Either the
149155 ** target slot in the parent is the right operand of a LEFT JOIN, or one of
149156 ** the left operands of a RIGHT JOIN. In either case, we need to potentially
149157 ** bypass the substituted expression with OP_IfNullRow.
149158 **
149159 ** Suppose the original expression is an integer constant. Even though the table
@@ -149987,21 +151092,16 @@
149987 ** elements we are now copying in.
149988 */
149989 pSub = pSub1;
149990 for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
149991 int nSubSrc;
149992 u8 jointype = 0;
149993 u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ;
149994 assert( pSub!=0 );
149995 pSubSrc = pSub->pSrc; /* FROM clause of subquery */
149996 nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
149997 pSrc = pParent->pSrc; /* FROM clause of the outer query */
149998
149999 if( pParent==p ){
150000 jointype = pSubitem->fg.jointype; /* First time through the loop */
150001 }
150002
150003 /* The subquery uses a single slot of the FROM clause of the outer
150004 ** query. If the subquery has more than one element in its FROM clause,
150005 ** then expand the outer query to make space for it to hold all elements
150006 ** of the subquery.
150007 **
@@ -150017,10 +151117,11 @@
150017 */
150018 if( nSubSrc>1 ){
150019 pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1);
150020 if( pSrc==0 ) break;
150021 pParent->pSrc = pSrc;
 
150022 }
150023
150024 /* Transfer the FROM clause terms from the subquery into the
150025 ** outer query.
150026 */
@@ -150031,15 +151132,14 @@
150031 assert( pItem->fg.isSubquery
150032 || pItem->fg.fixedSchema
150033 || pItem->u4.zDatabase==0 );
150034 if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
150035 *pItem = pSubSrc->a[i];
150036 pItem->fg.jointype |= ltorj;
150037 memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
150038 }
150039 pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
150040 pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
150041
150042 /* Now begin substituting subquery result set expressions for
150043 ** references to the iParent in the outer query.
150044 **
150045 ** Example:
@@ -152746,10 +153846,11 @@
152746 Select *pSub = pWhere->x.pSelect;
152747 Expr *pSubWhere = pSub->pWhere;
152748 if( pSub->pSrc->nSrc==1
152749 && (pSub->selFlags & SF_Aggregate)==0
152750 && !pSub->pSrc->a[0].fg.isSubquery
 
152751 ){
152752 memset(pWhere, 0, sizeof(*pWhere));
152753 pWhere->op = TK_INTEGER;
152754 pWhere->u.iValue = 1;
152755 ExprSetProperty(pWhere, EP_IntValue);
@@ -152774,10 +153875,119 @@
152774 existsToJoin(pParse, p, pSubWhere);
152775 }
152776 }
152777 }
152778 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152779
152780 /*
152781 ** Generate byte-code for the SELECT statement given in the p argument.
152782 **
152783 ** The results are returned according to the SelectDest structure.
@@ -152901,10 +154111,22 @@
152901 if( sqlite3TreeTrace & 0x10 ){
152902 TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
152903 sqlite3TreeViewSelect(0, p, 0);
152904 }
152905 #endif
 
 
 
 
 
 
 
 
 
 
 
 
152906
152907 /* If the SF_UFSrcCheck flag is set, then this function is being called
152908 ** as part of populating the temp table for an UPDATE...FROM statement.
152909 ** In this case, it is an error if the target object (pSrc->a[0]) name
152910 ** or alias is duplicated within FROM clause (pSrc->a[1..n]).
@@ -153766,10 +154988,11 @@
153766 iBMem = pParse->nMem + 1;
153767 pParse->nMem += pGroupBy->nExpr;
153768 sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
153769 VdbeComment((v, "clear abort flag"));
153770 sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
 
153771
153772 /* Begin a loop that will extract all source rows in GROUP BY order.
153773 ** This might involve two separate loops with an OP_Sort in between, or
153774 ** it might be a single loop that uses an index to extract information
153775 ** in the right order to begin with.
@@ -155466,11 +156689,14 @@
155466 sqlite3 *db = pParse->db;
155467 ExprList *pNew;
155468 Returning *pReturning;
155469 Select sSelect;
155470 SrcList *pFrom;
155471 u8 fromSpace[SZ_SRCLIST_1];
 
 
 
155472
155473 assert( v!=0 );
155474 if( !pParse->bReturning ){
155475 /* This RETURNING trigger must be for a different statement as
155476 ** this statement lacks a RETURNING clause. */
@@ -155482,12 +156708,12 @@
155482 if( pTrigger != &(pReturning->retTrig) ){
155483 /* This RETURNING trigger is for a different statement */
155484 return;
155485 }
155486 memset(&sSelect, 0, sizeof(sSelect));
155487 pFrom = (SrcList*)fromSpace;
155488 memset(pFrom, 0, SZ_SRCLIST_1);
155489 sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
155490 sSelect.pSrc = pFrom;
155491 pFrom->nSrc = 1;
155492 pFrom->a[0].pSTab = pTab;
155493 pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
@@ -163014,11 +164240,14 @@
163014 WhereClause *pWC = &pWInfo->sWC;
163015 WhereInfo *pSubWInfo;
163016 WhereLoop *pLoop = pLevel->pWLoop;
163017 SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
163018 SrcList *pFrom;
163019 u8 fromSpace[SZ_SRCLIST_1];
 
 
 
163020 Bitmask mAll = 0;
163021 int k;
163022
163023 ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
163024 sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -163058,11 +164287,11 @@
163058 if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
163059 pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
163060 sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
163061 }
163062 }
163063 pFrom = (SrcList*)fromSpace;
163064 pFrom->nSrc = 1;
163065 pFrom->nAlloc = 1;
163066 memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
163067 pFrom->a[0].fg.jointype = 0;
163068 assert( pParse->withinRJSubrtn < 100 );
@@ -164275,25 +165504,11 @@
164275 Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
164276 if( ExprHasProperty(pExpr, EP_OuterON) ){
164277 prereqAll |= x;
164278 extraRight = x-1; /* ON clause terms may not be used with an index
164279 ** on left table of a LEFT JOIN. Ticket #3015 */
164280 if( (prereqAll>>1)>=x ){
164281 sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
164282 return;
164283 }
164284 }else if( (prereqAll>>1)>=x ){
164285 /* The ON clause of an INNER JOIN references a table to its right.
164286 ** Most other SQL database engines raise an error. But SQLite versions
164287 ** 3.0 through 3.38 just put the ON clause constraint into the WHERE
164288 ** clause and carried on. Beginning with 3.39, raise an error only
164289 ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite
164290 ** more like other systems, and also preserves legacy. */
164291 if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
164292 sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
164293 return;
164294 }
164295 ExprClearProperty(pExpr, EP_InnerON);
164296 }
164297 }
164298 pTerm->prereqAll = prereqAll;
164299 pTerm->leftCursor = -1;
@@ -169084,10 +170299,11 @@
169084 if( pProbe->bNoQuery ) continue;
169085 rSize = pProbe->aiRowLogEst[0];
169086 pNew->u.btree.nEq = 0;
169087 pNew->u.btree.nBtm = 0;
169088 pNew->u.btree.nTop = 0;
 
169089 pNew->nSkip = 0;
169090 pNew->nLTerm = 0;
169091 pNew->iSortIdx = 0;
169092 pNew->rSetup = 0;
169093 pNew->prereq = mPrereq;
@@ -170150,14 +171366,16 @@
170150 if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
170151 if( pLoop->u.vtab.isOrdered
170152 && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
170153 ){
170154 obSat = obDone;
 
 
 
 
170155 }
170156 break;
170157 }else if( wctrlFlags & WHERE_DISTINCTBY ){
170158 pLoop->u.btree.nDistinctCol = 0;
170159 }
170160 iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
170161
170162 /* Mark off any ORDER BY term X that is a column in the table of
170163 ** the current loop for which there is term in the WHERE
@@ -170897,21 +172115,28 @@
170897
170898 /* Check to see if pWLoop should be added to the set of
170899 ** mxChoice best-so-far paths.
170900 **
170901 ** First look for an existing path among best-so-far paths
170902 ** that covers the same set of loops and has the same isOrdered
170903 ** setting as the current path candidate.
 
 
 
 
 
 
 
170904 **
170905 ** The term "((pTo->isOrdered^isOrdered)&0x80)==0" is equivalent
170906 ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range
170907 ** of legal values for isOrdered, -1..64.
170908 */
170909 testcase( nTo==0 );
170910 for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
170911 if( pTo->maskLoop==maskNew
170912 && ((pTo->isOrdered^isOrdered)&0x80)==0
170913 ){
170914 testcase( jj==nTo-1 );
170915 break;
170916 }
170917 }
@@ -171062,15 +172287,14 @@
171062 sqlite3ErrorMsg(pParse, "no query solution");
171063 sqlite3StackFreeNN(pParse->db, pSpace);
171064 return SQLITE_ERROR;
171065 }
171066
171067 /* Find the lowest cost path. pFrom will be left pointing to that path */
 
171068 pFrom = aFrom;
171069 for(ii=1; ii<nFrom; ii++){
171070 if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii];
171071 }
171072 assert( pWInfo->nLevel==nLoop );
171073 /* Load the lowest cost path into pWInfo */
171074 for(iLoop=0; iLoop<nLoop; iLoop++){
171075 WhereLevel *pLevel = pWInfo->a + iLoop;
171076 pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
@@ -171199,11 +172423,14 @@
171199 int once = 0;
171200 #endif
171201 for(i=0; i<pWInfo->nLevel; i++){
171202 WhereLoop *p = pWInfo->a[i].pWLoop;
171203 if( p==0 ) break;
171204 if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue;
 
 
 
171205 if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){
171206 u8 iTab = p->iTab;
171207 WhereLoop *pLoop;
171208 for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){
171209 if( pLoop->iTab!=iTab ) continue;
@@ -175312,11 +176539,11 @@
175312 ** RETURN_ROW
175313 **
175314 **
175315 ** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
175316 **
175317 ** ... loop started by sqlite3WhereBegin() ...
175318 ** if( new partition ){
175319 ** Gosub flush
175320 ** }
175321 ** Insert new row into eph table.
175322 ** if( first row of partition ){
@@ -175830,10 +177057,16 @@
175830 addrStart = sqlite3VdbeCurrentAddr(v);
175831 addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
175832 addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
175833 }else{
175834 assert( pMWin->eEnd==TK_FOLLOWING );
 
 
 
 
 
 
175835 addrStart = sqlite3VdbeCurrentAddr(v);
175836 addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
175837 addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
175838 }
175839 sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
@@ -183317,13 +184550,10 @@
183317 #endif
183318 #ifdef SQLITE_ENABLE_DBSTAT_VTAB
183319 sqlite3DbstatRegister,
183320 #endif
183321 sqlite3TestExtInit,
183322 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
183323 sqlite3JsonTableFunctions,
183324 #endif
183325 #ifdef SQLITE_ENABLE_STMTVTAB
183326 sqlite3StmtVtabInit,
183327 #endif
183328 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
183329 sqlite3VdbeBytecodeVtabInit,
@@ -184775,10 +186005,13 @@
184775 for(i=0; i<2 && zName==0; i++, rc &= 0xff){
184776 switch( rc ){
184777 case SQLITE_OK: zName = "SQLITE_OK"; break;
184778 case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
184779 case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
 
 
 
184780 case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
184781 case SQLITE_PERM: zName = "SQLITE_PERM"; break;
184782 case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
184783 case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
184784 case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
@@ -185955,10 +187188,33 @@
185955 }
185956 }
185957 sqlite3_mutex_leave(db->mutex);
185958 return z;
185959 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185960
185961 /*
185962 ** Return the byte offset of the most recent error
185963 */
185964 SQLITE_API int sqlite3_error_offset(sqlite3 *db){
@@ -187780,17 +189036,19 @@
187780 case SQLITE_TESTCTRL_ISINIT: {
187781 if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
187782 break;
187783 }
187784
187785 /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum);
187786 **
187787 ** This test control is used to create imposter tables. "db" is a pointer
187788 ** to the database connection. dbName is the database name (ex: "main" or
187789 ** "temp") which will receive the imposter. "onOff" turns imposter mode on
187790 ** or off. "tnum" is the root page of the b-tree to which the imposter
187791 ** table should connect.
 
 
187792 **
187793 ** Enable imposter mode only when the schema has already been parsed. Then
187794 ** run a single CREATE TABLE statement to construct the imposter table in
187795 ** the parsed schema. Then turn imposter mode back off again.
187796 **
@@ -189023,21 +190281,24 @@
189023 **
189024 */
189025 #ifndef _FTSINT_H
189026 #define _FTSINT_H
189027
 
 
 
 
 
 
 
189028 /* #include <assert.h> */
189029 /* #include <stdlib.h> */
189030 /* #include <stddef.h> */
189031 /* #include <stdio.h> */
189032 /* #include <string.h> */
189033 /* #include <stdarg.h> */
189034
189035 #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
189036 # define NDEBUG 1
189037 #endif
189038
189039 /* FTS3/FTS4 require virtual tables */
189040 #ifdef SQLITE_OMIT_VIRTUALTABLE
189041 # undef SQLITE_ENABLE_FTS3
189042 # undef SQLITE_ENABLE_FTS4
189043 #endif
@@ -189476,17 +190737,10 @@
189476 /*
189477 ** Macro used to suppress compiler warnings for unused parameters.
189478 */
189479 #define UNUSED_PARAMETER(x) (void)(x)
189480
189481 /*
189482 ** Activate assert() only if SQLITE_TEST is enabled.
189483 */
189484 #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
189485 # define NDEBUG 1
189486 #endif
189487
189488 /*
189489 ** The TESTONLY macro is used to enclose variable declarations or
189490 ** other bits of code that are needed to support the arguments
189491 ** within testcase() and assert() macros.
189492 */
@@ -203757,12 +205011,12 @@
203757 /*
203758 ** An object of this type contains the state required to create or append
203759 ** to an appendable b-tree segment.
203760 */
203761 struct IncrmergeWriter {
203762 int nLeafEst; /* Space allocated for leaf blocks */
203763 int nWork; /* Number of leaf pages flushed */
203764 sqlite3_int64 iAbsLevel; /* Absolute level of input segments */
203765 int iIdx; /* Index of *output* segment in iAbsLevel+1 */
203766 sqlite3_int64 iStart; /* Block number of first allocated block */
203767 sqlite3_int64 iEnd; /* Block number of last allocated block */
203768 sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
@@ -204504,21 +205758,21 @@
204504 Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */
204505 IncrmergeWriter *pWriter /* Populate this object */
204506 ){
204507 int rc; /* Return Code */
204508 int i; /* Iterator variable */
204509 int nLeafEst = 0; /* Blocks allocated for leaf nodes */
204510 sqlite3_stmt *pLeafEst = 0; /* SQL used to determine nLeafEst */
204511 sqlite3_stmt *pFirstBlock = 0; /* SQL used to determine first block */
204512
204513 /* Calculate nLeafEst. */
204514 rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0);
204515 if( rc==SQLITE_OK ){
204516 sqlite3_bind_int64(pLeafEst, 1, iAbsLevel);
204517 sqlite3_bind_int64(pLeafEst, 2, pCsr->nSegment);
204518 if( SQLITE_ROW==sqlite3_step(pLeafEst) ){
204519 nLeafEst = sqlite3_column_int(pLeafEst, 0);
204520 }
204521 rc = sqlite3_reset(pLeafEst);
204522 }
204523 if( rc!=SQLITE_OK ) return rc;
204524
@@ -205897,14 +207151,10 @@
205897 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
205898
205899 /* #include <string.h> */
205900 /* #include <assert.h> */
205901
205902 #ifndef SQLITE_AMALGAMATION
205903 typedef sqlite3_int64 i64;
205904 #endif
205905
205906 /*
205907 ** Characters that may appear in the second argument to matchinfo().
205908 */
205909 #define FTS3_MATCHINFO_NPHRASE 'p' /* 1 value */
205910 #define FTS3_MATCHINFO_NCOL 'c' /* 1 value */
@@ -210754,11 +212004,11 @@
210754 switch( (u8)zIn[1] ){
210755 case '\'':
210756 jsonAppendChar(pOut, '\'');
210757 break;
210758 case 'v':
210759 jsonAppendRawNZ(pOut, "\\u0009", 6);
210760 break;
210761 case 'x':
210762 if( sz2<4 ){
210763 pOut->eErr |= JSTRING_MALFORMED;
210764 sz2 = 2;
@@ -211604,23 +212854,31 @@
211604 /*
211605 ** Return the value of the BLOB node at index i.
211606 **
211607 ** If the value is a primitive, return it as an SQL value.
211608 ** If the value is an array or object, return it as either
211609 ** JSON text or the BLOB encoding, depending on the JSON_B flag
211610 ** on the userdata.
 
 
 
 
 
 
 
211611 */
211612 static void jsonReturnFromBlob(
211613 JsonParse *pParse, /* Complete JSON parse tree */
211614 u32 i, /* Index of the node */
211615 sqlite3_context *pCtx, /* Return value for this function */
211616 int textOnly /* return text JSON. Disregard user-data */
211617 ){
211618 u32 n, sz;
211619 int rc;
211620 sqlite3 *db = sqlite3_context_db_handle(pCtx);
211621
 
211622 n = jsonbPayloadSize(pParse, i, &sz);
211623 if( n==0 ){
211624 sqlite3_result_error(pCtx, "malformed JSON", -1);
211625 return;
211626 }
@@ -211657,11 +212915,23 @@
211657 z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
211658 if( z==0 ) goto returnfromblob_oom;
211659 rc = sqlite3DecOrHexToI64(z, &iRes);
211660 sqlite3DbFree(db, z);
211661 if( rc==0 ){
211662 sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
 
 
 
 
 
 
 
 
 
 
 
 
211663 }else if( rc==3 && bNeg ){
211664 sqlite3_result_int64(pCtx, SMALLEST_INT64);
211665 }else if( rc==1 ){
211666 goto returnfromblob_malformed;
211667 }else{
@@ -211735,12 +213005,18 @@
211735 sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
211736 break;
211737 }
211738 case JSONB_ARRAY:
211739 case JSONB_OBJECT: {
211740 int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
211741 if( flags & JSON_BLOB ){
 
 
 
 
 
 
211742 sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
211743 }else{
211744 jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
211745 }
211746 break;
@@ -213383,10 +214659,11 @@
213383 u32 i; /* Index in sParse.aBlob[] of current row */
213384 u32 iEnd; /* EOF when i equals or exceeds this value */
213385 u32 nRoot; /* Size of the root path in bytes */
213386 u8 eType; /* Type of the container for element i */
213387 u8 bRecursive; /* True for json_tree(). False for json_each() */
 
213388 u32 nParent; /* Current nesting depth */
213389 u32 nParentAlloc; /* Space allocated for aParent[] */
213390 JsonParent *aParent; /* Parent elements of i */
213391 sqlite3 *db; /* Database connection */
213392 JsonString path; /* Current path */
@@ -213394,10 +214671,12 @@
213394 };
213395 typedef struct JsonEachConnection JsonEachConnection;
213396 struct JsonEachConnection {
213397 sqlite3_vtab base; /* Base class - must be first */
213398 sqlite3 *db; /* Database connection */
 
 
213399 };
213400
213401
213402 /* Constructor for the json_each virtual table */
213403 static int jsonEachConnect(
@@ -213436,10 +214715,12 @@
213436 pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
213437 *ppVtab = (sqlite3_vtab*)pNew;
213438 if( pNew==0 ) return SQLITE_NOMEM;
213439 sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
213440 pNew->db = db;
 
 
213441 }
213442 return rc;
213443 }
213444
213445 /* destructor for json_each virtual table */
@@ -213447,34 +214728,26 @@
213447 JsonEachConnection *p = (JsonEachConnection*)pVtab;
213448 sqlite3DbFree(p->db, pVtab);
213449 return SQLITE_OK;
213450 }
213451
213452 /* constructor for a JsonEachCursor object for json_each(). */
213453 static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
213454 JsonEachConnection *pVtab = (JsonEachConnection*)p;
213455 JsonEachCursor *pCur;
213456
213457 UNUSED_PARAMETER(p);
213458 pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
213459 if( pCur==0 ) return SQLITE_NOMEM;
213460 pCur->db = pVtab->db;
 
 
213461 jsonStringZero(&pCur->path);
213462 *ppCursor = &pCur->base;
213463 return SQLITE_OK;
213464 }
213465
213466 /* constructor for a JsonEachCursor object for json_tree(). */
213467 static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
213468 int rc = jsonEachOpenEach(p, ppCursor);
213469 if( rc==SQLITE_OK ){
213470 JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
213471 pCur->bRecursive = 1;
213472 }
213473 return rc;
213474 }
213475
213476 /* Reset a JsonEachCursor back to its original state. Free any memory
213477 ** held. */
213478 static void jsonEachCursorReset(JsonEachCursor *p){
213479 jsonParseReset(&p->sParse);
213480 jsonStringReset(&p->path);
@@ -213675,11 +214948,11 @@
213675 }
213676 break;
213677 }
213678 case JEACH_VALUE: {
213679 u32 i = jsonSkipLabel(p);
213680 jsonReturnFromBlob(&p->sParse, i, ctx, 1);
213681 if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
213682 sqlite3_result_subtype(ctx, JSON_SUBTYPE);
213683 }
213684 break;
213685 }
@@ -213919,40 +215192,11 @@
213919 0, /* xCreate */
213920 jsonEachConnect, /* xConnect */
213921 jsonEachBestIndex, /* xBestIndex */
213922 jsonEachDisconnect, /* xDisconnect */
213923 0, /* xDestroy */
213924 jsonEachOpenEach, /* xOpen - open a cursor */
213925 jsonEachClose, /* xClose - close a cursor */
213926 jsonEachFilter, /* xFilter - configure scan constraints */
213927 jsonEachNext, /* xNext - advance a cursor */
213928 jsonEachEof, /* xEof - check for end of scan */
213929 jsonEachColumn, /* xColumn - read data */
213930 jsonEachRowid, /* xRowid - read data */
213931 0, /* xUpdate */
213932 0, /* xBegin */
213933 0, /* xSync */
213934 0, /* xCommit */
213935 0, /* xRollback */
213936 0, /* xFindMethod */
213937 0, /* xRename */
213938 0, /* xSavepoint */
213939 0, /* xRelease */
213940 0, /* xRollbackTo */
213941 0, /* xShadowName */
213942 0 /* xIntegrity */
213943 };
213944
213945 /* The methods of the json_tree virtual table. */
213946 static sqlite3_module jsonTreeModule = {
213947 0, /* iVersion */
213948 0, /* xCreate */
213949 jsonEachConnect, /* xConnect */
213950 jsonEachBestIndex, /* xBestIndex */
213951 jsonEachDisconnect, /* xDisconnect */
213952 0, /* xDestroy */
213953 jsonEachOpenTree, /* xOpen - open a cursor */
213954 jsonEachClose, /* xClose - close a cursor */
213955 jsonEachFilter, /* xFilter - configure scan constraints */
213956 jsonEachNext, /* xNext - advance a cursor */
213957 jsonEachEof, /* xEof - check for end of scan */
213958 jsonEachColumn, /* xColumn - read data */
@@ -214037,26 +215281,25 @@
214037 #endif
214038 }
214039
214040 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
214041 /*
214042 ** Register the JSON table-valued functions
 
214043 */
214044 SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3 *db){
214045 int rc = SQLITE_OK;
214046 static const struct {
214047 const char *zName;
214048 sqlite3_module *pModule;
214049 } aMod[] = {
214050 { "json_each", &jsonEachModule },
214051 { "json_tree", &jsonTreeModule },
214052 };
214053 unsigned int i;
214054 for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
214055 rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
 
 
 
 
 
 
214056 }
214057 return rc;
214058 }
214059 #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) */
214060
214061 /************** End of json.c ************************************************/
214062 /************** Begin file rtree.c *******************************************/
@@ -228289,12 +229532,12 @@
228289 typedef struct DbpageTable DbpageTable;
228290 typedef struct DbpageCursor DbpageCursor;
228291
228292 struct DbpageCursor {
228293 sqlite3_vtab_cursor base; /* Base class. Must be first */
228294 int pgno; /* Current page number */
228295 int mxPgno; /* Last page to visit on this scan */
228296 Pager *pPager; /* Pager being read/written */
228297 DbPage *pPage1; /* Page 1 of the database */
228298 int iDb; /* Index of database to analyze */
228299 int szPage; /* Size of each page in bytes */
228300 };
@@ -228427,11 +229670,11 @@
228427 if( pCsr==0 ){
228428 return SQLITE_NOMEM_BKPT;
228429 }else{
228430 memset(pCsr, 0, sizeof(DbpageCursor));
228431 pCsr->base.pVtab = pVTab;
228432 pCsr->pgno = -1;
228433 }
228434
228435 *ppCursor = (sqlite3_vtab_cursor *)pCsr;
228436 return SQLITE_OK;
228437 }
@@ -228527,16 +229770,16 @@
228527 ){
228528 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
228529 int rc = SQLITE_OK;
228530 switch( i ){
228531 case 0: { /* pgno */
228532 sqlite3_result_int(ctx, pCsr->pgno);
228533 break;
228534 }
228535 case 1: { /* data */
228536 DbPage *pDbPage = 0;
228537 if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
228538 /* The pending byte page. Assume it is zeroed out. Attempting to
228539 ** request this page from the page is an SQLITE_CORRUPT error. */
228540 sqlite3_result_zeroblob(ctx, pCsr->szPage);
228541 }else{
228542 rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
@@ -228606,14 +229849,14 @@
228606 if( argc==1 ){
228607 zErr = "cannot delete";
228608 goto update_fail;
228609 }
228610 if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
228611 pgno = (Pgno)sqlite3_value_int(argv[2]);
228612 isInsert = 1;
228613 }else{
228614 pgno = sqlite3_value_int(argv[0]);
228615 if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
228616 zErr = "cannot insert";
228617 goto update_fail;
228618 }
228619 isInsert = 0;
@@ -228744,10 +229987,539 @@
228744 #elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
228745 SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
228746 #endif /* SQLITE_ENABLE_DBSTAT_VTAB */
228747
228748 /************** End of dbpage.c **********************************************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228749 /************** Begin file sqlite3session.c **********************************/
228750
228751 #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
228752 /* #include "sqlite3session.h" */
228753 /* #include <assert.h> */
@@ -231561,10 +233333,23 @@
231561 assert( (a - p->aRecord)==p->nRecord );
231562 }
231563
231564 return rc;
231565 }
 
 
 
 
 
 
 
 
 
 
 
 
 
231566
231567 /*
231568 ** Formulate and prepare a SELECT statement to retrieve a row from table
231569 ** zTab in database zDb based on its primary key. i.e.
231570 **
@@ -231583,16 +233368,16 @@
231583 const char *zTab, /* Table name */
231584 int bRowid,
231585 int nCol, /* Number of columns in table */
231586 const char **azCol, /* Names of table columns */
231587 u8 *abPK, /* PRIMARY KEY array */
231588 sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
 
231589 ){
231590 int rc = SQLITE_OK;
231591 char *zSql = 0;
231592 const char *zSep = "";
231593 int nSql = -1;
231594 int i;
231595
231596 SessionBuffer cols = {0, 0, 0};
231597 SessionBuffer nooptest = {0, 0, 0};
231598 SessionBuffer pkfield = {0, 0, 0};
@@ -231668,11 +233453,11 @@
231668 nSql = buf.nBuf;
231669 }
231670 #endif
231671
231672 if( rc==SQLITE_OK ){
231673 rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0);
231674 }
231675 sqlite3_free(zSql);
231676 sqlite3_free(nooptest.aBuf);
231677 sqlite3_free(pkfield.aBuf);
231678 sqlite3_free(pkvar.aBuf);
@@ -231832,11 +233617,11 @@
231832 sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
231833
231834 /* Build and compile a statement to execute: */
231835 if( rc==SQLITE_OK ){
231836 rc = sessionSelectStmt(db, 0, pSession->zDb,
231837 zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel
231838 );
231839 }
231840
231841 nNoop = buf.nBuf;
231842 for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
@@ -233041,10 +234826,11 @@
233041 SessionBuffer rebase; /* Rebase information (if any) here */
233042 u8 bRebaseStarted; /* If table header is already in rebase */
233043 u8 bRebase; /* True to collect rebase information */
233044 u8 bIgnoreNoop; /* True to ignore no-op conflicts */
233045 int bRowid;
 
233046 };
233047
233048 /* Number of prepared UPDATE statements to cache. */
233049 #define SESSION_UPDATE_CACHE_SZ 12
233050
@@ -233266,11 +235052,11 @@
233266 }
233267 sessionAppendStr(&buf, ")", &rc);
233268 }
233269
233270 if( rc==SQLITE_OK ){
233271 rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
233272 }
233273 sqlite3_free(buf.aBuf);
233274
233275 return rc;
233276 }
@@ -233293,11 +235079,11 @@
233293 const char *zTab, /* Table name */
233294 SessionApplyCtx *p /* Session changeset-apply context */
233295 ){
233296 /* TODO */
233297 return sessionSelectStmt(db, p->bIgnoreNoop,
233298 "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect
233299 );
233300 }
233301
233302 /*
233303 ** Formulate and prepare an INSERT statement to add a record to table zTab.
@@ -233330,37 +235116,33 @@
233330 sessionAppendStr(&buf, ", ?", &rc);
233331 }
233332 sessionAppendStr(&buf, ")", &rc);
233333
233334 if( rc==SQLITE_OK ){
233335 rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
233336 }
233337 sqlite3_free(buf.aBuf);
233338 return rc;
233339 }
233340
233341 static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){
233342 return sqlite3_prepare_v2(db, zSql, -1, pp, 0);
233343 }
233344
233345 /*
233346 ** Prepare statements for applying changes to the sqlite_stat1 table.
233347 ** These are similar to those created by sessionSelectRow(),
233348 ** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
233349 ** other tables.
233350 */
233351 static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
233352 int rc = sessionSelectRow(db, "sqlite_stat1", p);
233353 if( rc==SQLITE_OK ){
233354 rc = sessionPrepare(db, &p->pInsert,
233355 "INSERT INTO main.sqlite_stat1 VALUES(?1, "
233356 "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, "
233357 "?3)"
233358 );
233359 }
233360 if( rc==SQLITE_OK ){
233361 rc = sessionPrepare(db, &p->pDelete,
233362 "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
233363 "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
233364 "AND (?4 OR stat IS ?3)"
233365 );
233366 }
@@ -233580,11 +235362,11 @@
233580 sqlite3_changeset_iter *pIter, /* Changeset iterator */
233581 int(*xConflict)(void *, int, sqlite3_changeset_iter*),
233582 void *pCtx, /* First argument for conflict handler */
233583 int *pbReplace /* OUT: Set to true if PK row is found */
233584 ){
233585 int res = 0; /* Value returned by conflict handler */
233586 int rc;
233587 int nCol;
233588 int op;
233589 const char *zDummy;
233590
@@ -233601,15 +235383,13 @@
233601 rc = SQLITE_OK;
233602 }
233603
233604 if( rc==SQLITE_ROW ){
233605 /* There exists another row with the new.* primary key. */
233606 if( p->bIgnoreNoop
233607 && sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
233608 ){
233609 res = SQLITE_CHANGESET_OMIT;
233610 }else{
233611 pIter->pConflict = p->pSelect;
233612 res = xConflict(pCtx, eType, pIter);
233613 pIter->pConflict = 0;
233614 }
233615 rc = sqlite3_reset(p->pSelect);
@@ -233619,11 +235399,13 @@
233619 ** to the SessionApplyCtx.constraints buffer. */
233620 u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
233621 int nBlob = pIter->in.iNext - pIter->in.iCurrent;
233622 sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
233623 return SQLITE_OK;
233624 }else{
 
 
233625 /* No other row with the new.* primary key. */
233626 res = xConflict(pCtx, eType+1, pIter);
233627 if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
233628 }
233629 }
@@ -233717,11 +235499,11 @@
233717 }
233718 if( rc!=SQLITE_OK ) return rc;
233719
233720 sqlite3_step(p->pDelete);
233721 rc = sqlite3_reset(p->pDelete);
233722 if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){
233723 rc = sessionConflictHandler(
233724 SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
233725 );
233726 }else if( (rc&0xff)==SQLITE_CONSTRAINT ){
233727 rc = sessionConflictHandler(
@@ -234122,10 +235904,11 @@
234122 }
234123 }
234124
234125 assert( sApply.bRebase || sApply.rebase.nBuf==0 );
234126 if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
 
234127 *ppRebase = (void*)sApply.rebase.aBuf;
234128 *pnRebase = sApply.rebase.nBuf;
234129 sApply.rebase.aBuf = 0;
234130 }
234131 sessionUpdateFree(&sApply);
@@ -234139,10 +235922,15 @@
234139 if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
234140 assert( db->flags & SQLITE_FkNoAction );
234141 db->flags &= ~((u64)SQLITE_FkNoAction);
234142 db->aDb[0].pSchema->schema_cookie -= 32;
234143 }
 
 
 
 
 
234144 sqlite3_mutex_leave(sqlite3_db_mutex(db));
234145 return rc;
234146 }
234147
234148 /*
@@ -236348,25 +238136,18 @@
236348 ** Constants for the largest and smallest possible 64-bit signed integers.
236349 */
236350 # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
236351 # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
236352
236353 /* The uptr type is an unsigned integer large enough to hold a pointer
 
 
 
 
 
236354 */
236355 #if defined(HAVE_STDINT_H)
236356 typedef uintptr_t uptr;
236357 #elif SQLITE_PTRSIZE==4
236358 typedef u32 uptr;
236359 #else
236360 typedef u64 uptr;
236361 #endif
236362
236363 #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
236364 # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
236365 #else
236366 # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
236367 #endif
236368
236369 /*
236370 ** Macros needed to provide flexible arrays in a portable way
236371 */
236372 #ifndef offsetof
@@ -237110,11 +238891,11 @@
237110 ** ){
237111 ** // The document with rowid iRowid matches the expression!
237112 ** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
237113 ** }
237114 */
237115 static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, int bDesc);
237116 static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax);
237117 static int sqlite3Fts5ExprEof(Fts5Expr*);
237118 static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
237119
237120 static void sqlite3Fts5ExprFree(Fts5Expr*);
@@ -242679,11 +244460,17 @@
242679 ** equal to iFirst.
242680 **
242681 ** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
242682 ** is not considered an error if the query does not match any documents.
242683 */
242684 static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
 
 
 
 
 
 
242685 Fts5ExprNode *pRoot = p->pRoot;
242686 int rc; /* Return code */
242687
242688 p->pIndex = pIdx;
242689 p->bDesc = bDesc;
@@ -242700,10 +244487,13 @@
242700
242701 /* If the iterator is not at a real match, skip forward until it is. */
242702 while( pRoot->bNomatch && rc==SQLITE_OK ){
242703 assert( pRoot->bEof==0 );
242704 rc = fts5ExprNodeNext(p, pRoot, 0, 0);
 
 
 
242705 }
242706 return rc;
242707 }
242708
242709 /*
@@ -245876,13 +247666,13 @@
245876 ** backing store corruption. */
245877 if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
245878
245879 if( rc==SQLITE_OK ){
245880 u8 *aOut = 0; /* Read blob data into this buffer */
245881 int nByte = sqlite3_blob_bytes(p->pReader);
245882 int szData = (sizeof(Fts5Data) + 7) & ~7;
245883 sqlite3_int64 nAlloc = szData + nByte + FTS5_DATA_PADDING;
245884 pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
245885 if( pRet ){
245886 pRet->nn = nByte;
245887 aOut = pRet->p = (u8*)pRet + szData;
245888 }else{
@@ -251821,15 +253611,18 @@
251821 ** function populates it with the initial structure objects for each index,
251822 ** and the initial version of the "averages" record (a zero-byte blob).
251823 */
251824 static int sqlite3Fts5IndexReinit(Fts5Index *p){
251825 Fts5Structure *pTmp;
251826 u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
 
 
 
251827 fts5StructureInvalidate(p);
251828 fts5IndexDiscardData(p);
251829 pTmp = (Fts5Structure*)tmpSpace;
251830 memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
251831 if( p->pConfig->bContentlessDelete ){
251832 pTmp->nOriginCntr = 1;
251833 }
251834 fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
251835 fts5StructureWrite(p, pTmp);
@@ -255045,10 +256838,21 @@
255045 {
255046 pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
255047 }
255048 #endif
255049 }
 
 
 
 
 
 
 
 
 
 
 
255050
255051 static int fts5UsePatternMatch(
255052 Fts5Config *pConfig,
255053 struct sqlite3_index_constraint *p
255054 ){
@@ -255181,11 +256985,11 @@
255181 bSeenRank = 1;
255182 }else{
255183 nSeenMatch++;
255184 idxStr[iIdxStr++] = 'M';
255185 sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
255186 idxStr += strlen(&idxStr[iIdxStr]);
255187 assert( idxStr[iIdxStr]=='\0' );
255188 }
255189 pInfo->aConstraintUsage[i].argvIndex = ++iCons;
255190 pInfo->aConstraintUsage[i].omit = 1;
255191 }
@@ -255200,10 +257004,11 @@
255200 nSeenMatch++;
255201 }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
255202 idxStr[iIdxStr++] = '=';
255203 bSeenEq = 1;
255204 pInfo->aConstraintUsage[i].argvIndex = ++iCons;
 
255205 }
255206 }
255207 }
255208
255209 if( bSeenEq==0 ){
@@ -255247,21 +257052,25 @@
255247 }
255248 }
255249
255250 /* Calculate the estimated cost based on the flags set in idxFlags. */
255251 if( bSeenEq ){
255252 pInfo->estimatedCost = nSeenMatch ? 1000.0 : 10.0;
255253 if( nSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
255254 }else if( bSeenLt && bSeenGt ){
255255 pInfo->estimatedCost = nSeenMatch ? 5000.0 : 250000.0;
255256 }else if( bSeenLt || bSeenGt ){
255257 pInfo->estimatedCost = nSeenMatch ? 7500.0 : 750000.0;
255258 }else{
255259 pInfo->estimatedCost = nSeenMatch ? 10000.0 : 1000000.0;
255260 }
255261 for(i=1; i<nSeenMatch; i++){
255262 pInfo->estimatedCost *= 0.4;
 
 
 
 
 
 
 
255263 }
255264
255265 pInfo->idxNum = idxFlags;
255266 return SQLITE_OK;
255267 }
@@ -255456,11 +257265,13 @@
255456 if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
255457 Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
255458 int bDesc = pCsr->bDesc;
255459 i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
255460
255461 rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc);
 
 
255462 if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
255463 *pbSkip = 1;
255464 }
255465
255466 CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
@@ -255628,11 +257439,13 @@
255628 }
255629
255630 static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
255631 int rc;
255632 Fts5Expr *pExpr = pCsr->pExpr;
255633 rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc);
 
 
255634 if( sqlite3Fts5ExprEof(pExpr) ){
255635 CsrFlagSet(pCsr, FTS5CSR_EOF);
255636 }
255637 fts5CsrNewrow(pCsr);
255638 return rc;
@@ -258113,11 +259926,11 @@
258113 int nArg, /* Number of args */
258114 sqlite3_value **apUnused /* Function arguments */
258115 ){
258116 assert( nArg==0 );
258117 UNUSED_PARAM2(nArg, apUnused);
258118 sqlite3_result_text(pCtx, "fts5: 2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839", -1, SQLITE_TRANSIENT);
258119 }
258120
258121 /*
258122 ** Implementation of fts5_locale(LOCALE, TEXT) function.
258123 **
@@ -258136,13 +259949,13 @@
258136 sqlite3_context *pCtx, /* Function call context */
258137 int nArg, /* Number of args */
258138 sqlite3_value **apArg /* Function arguments */
258139 ){
258140 const char *zLocale = 0;
258141 int nLocale = 0;
258142 const char *zText = 0;
258143 int nText = 0;
258144
258145 assert( nArg==2 );
258146 UNUSED_PARAM(nArg);
258147
258148 zLocale = (const char*)sqlite3_value_text(apArg[0]);
@@ -258155,14 +259968,14 @@
258155 sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
258156 }else{
258157 Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx);
258158 u8 *pBlob = 0;
258159 u8 *pCsr = 0;
258160 int nBlob = 0;
258161
258162 nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText;
258163 pBlob = (u8*)sqlite3_malloc(nBlob);
258164 if( pBlob==0 ){
258165 sqlite3_result_error_nomem(pCtx);
258166 return;
258167 }
258168
258169
--- 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 ** 5cbccab499bc3983aac1f57355552db607de with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -168,11 +168,13 @@
168 #define SQLITE_OMIT_LOAD_EXTENSION 1
169 #define SQLITE_ENABLE_LOCKING_STYLE 0
170 #define HAVE_UTIME 1
171 #else
172 /* This is not VxWorks. */
173 #ifndef OS_VXWORKS
174 # define OS_VXWORKS 0
175 #endif
176 #define HAVE_FCHOWN 1
177 #define HAVE_READLINK 1
178 #define HAVE_LSTAT 1
179 #endif /* defined(_WRS_KERNEL) */
180
@@ -465,11 +467,14 @@
467 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
468 ** [sqlite_version()] and [sqlite_source_id()].
469 */
470 #define SQLITE_VERSION "3.51.0"
471 #define SQLITE_VERSION_NUMBER 3051000
472 #define SQLITE_SOURCE_ID "2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70"
473 #define SQLITE_SCM_BRANCH "trunk"
474 #define SQLITE_SCM_TAGS ""
475 #define SQLITE_SCM_DATETIME "2025-10-15T10:52:45.276Z"
476
477 /*
478 ** CAPI3REF: Run-Time Library Version Numbers
479 ** KEYWORDS: sqlite3_version sqlite3_sourceid
480 **
@@ -814,10 +819,13 @@
819 ** [sqlite3_extended_errcode()].
820 */
821 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
822 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
823 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
824 #define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
825 #define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
826 #define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
827 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
828 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
829 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
830 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
831 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -848,10 +856,12 @@
856 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
857 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
858 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
859 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
860 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
861 #define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
862 #define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
863 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
864 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
865 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
866 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
867 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -2652,21 +2662,24 @@
2662 ** views in the main database schema or in the schemas of ATTACH-ed
2663 ** databases.)^ </dd>
2664 **
2665 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
2666 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2667 ** <dd> ^This option is used to enable or disable using the
2668 ** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine
2669 ** extension - without using bound parameters as the parameters. Doing so
2670 ** is disabled by default. There must be two additional arguments. The first
2671 ** argument is an integer. If it is passed 0, then using fts3_tokenizer()
2672 ** without bound parameters is disabled. If it is passed a positive value,
2673 ** then calling fts3_tokenizer without bound parameters is enabled. If it
2674 ** is passed a negative value, this setting is not modified - this can be
2675 ** used to query for the current setting. The second parameter is a pointer
2676 ** to an integer into which is written 0 or 1 to indicate the current value
2677 ** of this setting (after it is modified, if applicable). The second
2678 ** parameter may be a NULL pointer, in which case the value of the setting
2679 ** is not reported back. Refer to [FTS3] documentation for further details.
2680 ** </dd>
2681 **
2682 ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
2683 ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
2684 ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
2685 ** interface independently of the [load_extension()] SQL function.
@@ -4512,10 +4525,38 @@
4525 SQLITE_API const char *sqlite3_errmsg(sqlite3*);
4526 SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
4527 SQLITE_API const char *sqlite3_errstr(int);
4528 SQLITE_API int sqlite3_error_offset(sqlite3 *db);
4529
4530 /*
4531 ** CAPI3REF: Set Error Codes And Message
4532 ** METHOD: sqlite3
4533 **
4534 ** Set the error code of the database handle passed as the first argument
4535 ** to errcode, and the error message to a copy of nul-terminated string
4536 ** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
4537 ** the default message associated with the supplied error code. Subsequent
4538 ** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
4539 ** return the values set by this routine in place of what was previously
4540 ** set by SQLite itself.
4541 **
4542 ** This function returns SQLITE_OK if the error code and error message are
4543 ** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
4544 ** the database handle is NULL or invalid.
4545 **
4546 ** The error code and message set by this routine remains in effect until
4547 ** they are changed, either by another call to this routine or until they are
4548 ** changed to by SQLite itself to reflect the result of some subsquent
4549 ** API call.
4550 **
4551 ** This function is intended for use by SQLite extensions or wrappers. The
4552 ** idea is that an extension or wrapper can use this routine to set error
4553 ** messages and error codes and thus behave more like a core SQLite
4554 ** feature from the point of view of an application.
4555 */
4556 SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
4557
4558 /*
4559 ** CAPI3REF: Prepared Statement Object
4560 ** KEYWORDS: {prepared statement} {prepared statements}
4561 **
4562 ** An instance of this object represents a single SQL statement that
@@ -6522,10 +6563,11 @@
6563 ** to be attached to [database connection] D using name N. Subsequent
6564 ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
6565 ** or a NULL pointer if there were no prior calls to
6566 ** sqlite3_set_clientdata() with the same values of D and N.
6567 ** Names are compared using strcmp() and are thus case sensitive.
6568 ** It returns 0 on success and SQLITE_NOMEM on allocation failure.
6569 **
6570 ** If P and X are both non-NULL, then the destructor X is invoked with
6571 ** argument P on the first of the following occurrences:
6572 ** <ul>
6573 ** <li> An out-of-memory error occurs during the call to
@@ -9197,14 +9239,23 @@
9239 ** the resetFlg is true, then the highest instantaneous value is
9240 ** reset back down to the current value.
9241 **
9242 ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
9243 ** non-zero [error code] on failure.
9244 **
9245 ** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
9246 ** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
9247 ** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
9248 ** of pointers to 32-bit integers, which allows larger status values
9249 ** to be returned. If a status value exceeds 2,147,483,647 then
9250 ** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
9251 ** will return the full value.
9252 **
9253 ** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
9254 */
9255 SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
9256 SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
9257
9258 /*
9259 ** CAPI3REF: Status Parameters for database connections
9260 ** KEYWORDS: {SQLITE_DBSTATUS options}
9261 **
@@ -9297,10 +9348,14 @@
9348 ** database file in rollback mode databases. Any pages written as part of
9349 ** transaction rollback or database recovery operations are not included.
9350 ** If an IO or other error occurs while writing a page to disk, the effect
9351 ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
9352 ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
9353 ** <p>
9354 ** ^(There is overlap between the quantities measured by this parameter
9355 ** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL.
9356 ** Resetting one will reduce the other.)^
9357 ** </dd>
9358 **
9359 ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
9360 ** <dd>This parameter returns the number of dirty cache entries that have
9361 ** been written to disk in the middle of a transaction due to the page
@@ -9312,10 +9367,22 @@
9367 **
9368 ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
9369 ** <dd>This parameter returns zero for the current value if and only if
9370 ** all foreign key constraints (deferred or immediate) have been
9371 ** resolved.)^ ^The highwater mark is always 0.
9372 **
9373 ** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt>
9374 ** <dd>^(This parameter returns the number of bytes written to temporary
9375 ** files on disk that could have been kept in memory had sufficient memory
9376 ** been available. This value includes writes to intermediate tables that
9377 ** are part of complex queries, external sorts that spill to disk, and
9378 ** writes to TEMP tables.)^
9379 ** ^The highwater mark is always 0.
9380 ** <p>
9381 ** ^(There is overlap between the quantities measured by this parameter
9382 ** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE.
9383 ** Resetting one will reduce the other.)^
9384 ** </dd>
9385 ** </dl>
9386 */
9387 #define SQLITE_DBSTATUS_LOOKASIDE_USED 0
9388 #define SQLITE_DBSTATUS_CACHE_USED 1
@@ -9328,11 +9395,12 @@
9395 #define SQLITE_DBSTATUS_CACHE_MISS 8
9396 #define SQLITE_DBSTATUS_CACHE_WRITE 9
9397 #define SQLITE_DBSTATUS_DEFERRED_FKS 10
9398 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
9399 #define SQLITE_DBSTATUS_CACHE_SPILL 12
9400 #define SQLITE_DBSTATUS_TEMPBUF_SPILL 13
9401 #define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */
9402
9403
9404 /*
9405 ** CAPI3REF: Prepared Statement Status
9406 ** METHOD: sqlite3_stmt
@@ -10093,25 +10161,38 @@
10161 ** ^The third parameter is the name of the database that was written to -
10162 ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
10163 ** is the number of pages currently in the write-ahead log file,
10164 ** including those that were just committed.
10165 **
10166 ** ^The callback function should normally return [SQLITE_OK]. ^If an error
10167 ** code is returned, that error will propagate back up through the
10168 ** SQLite code base to cause the statement that provoked the callback
10169 ** to report an error, though the commit will have still occurred. If the
10170 ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
10171 ** that does not correspond to any valid SQLite error code, the results
10172 ** are undefined.
10173 **
10174 ** ^A single database handle may have at most a single write-ahead log
10175 ** callback registered at one time. ^Calling [sqlite3_wal_hook()]
10176 ** replaces the default behavior or previously registered write-ahead
10177 ** log callback.
10178 **
10179 ** ^The return value is a copy of the third parameter from the
10180 ** previous call, if any, or 0.
10181 **
10182 ** ^The [sqlite3_wal_autocheckpoint()] interface and the
10183 ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and
10184 ** will overwrite any prior [sqlite3_wal_hook()] settings.
10185 **
10186 ** ^If a write-ahead log callback is set using this function then
10187 ** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint]
10188 ** should be invoked periodically to keep the write-ahead log file
10189 ** from growing without bound.
10190 **
10191 ** ^Passing a NULL pointer for the callback disables automatic
10192 ** checkpointing entirely. To re-enable the default behavior, call
10193 ** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint].
10194 */
10195 SQLITE_API void *sqlite3_wal_hook(
10196 sqlite3*,
10197 int(*)(void *,sqlite3*,const char*,int),
10198 void*
@@ -10124,11 +10205,11 @@
10205 ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
10206 ** [sqlite3_wal_hook()] that causes any database on [database connection] D
10207 ** to automatically [checkpoint]
10208 ** after committing a transaction if there are N or
10209 ** more frames in the [write-ahead log] file. ^Passing zero or
10210 ** a negative value as the N parameter disables automatic
10211 ** checkpoints entirely.
10212 **
10213 ** ^The callback registered by this function replaces any existing callback
10214 ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback
10215 ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
@@ -10140,13 +10221,14 @@
10221 ** ^Checkpoints initiated by this mechanism are
10222 ** [sqlite3_wal_checkpoint_v2|PASSIVE].
10223 **
10224 ** ^Every new [database connection] defaults to having the auto-checkpoint
10225 ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
10226 ** pages.
10227 **
10228 ** ^The use of this interface is only necessary if the default setting
10229 ** is found to be suboptimal for a particular application.
10230 */
10231 SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
10232
10233 /*
10234 ** CAPI3REF: Checkpoint a database
@@ -10207,10 +10289,15 @@
10289 **
10290 ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
10291 ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
10292 ** addition that it also truncates the log file to zero bytes just prior
10293 ** to a successful return.
10294 **
10295 ** <dt>SQLITE_CHECKPOINT_NOOP<dd>
10296 ** ^This mode always checkpoints zero frames. The only reason to invoke
10297 ** a NOOP checkpoint is to access the values returned by
10298 ** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt.
10299 ** </dl>
10300 **
10301 ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
10302 ** the log file or to -1 if the checkpoint could not run because
10303 ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
@@ -10277,10 +10364,11 @@
10364 ** These constants define all valid values for the "checkpoint mode" passed
10365 ** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
10366 ** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
10367 ** meaning of each of these checkpoint modes.
10368 */
10369 #define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
10370 #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
10371 #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
10372 #define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
10373 #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
10374
@@ -11104,11 +11192,11 @@
11192 ** to avoid a memory leak.
11193 **
11194 ** The [sqlite3_snapshot_get()] interface is only available when the
11195 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
11196 */
11197 SQLITE_API int sqlite3_snapshot_get(
11198 sqlite3 *db,
11199 const char *zSchema,
11200 sqlite3_snapshot **ppSnapshot
11201 );
11202
@@ -11153,11 +11241,11 @@
11241 ** database connection in order to make it ready to use snapshots.)
11242 **
11243 ** The [sqlite3_snapshot_open()] interface is only available when the
11244 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
11245 */
11246 SQLITE_API int sqlite3_snapshot_open(
11247 sqlite3 *db,
11248 const char *zSchema,
11249 sqlite3_snapshot *pSnapshot
11250 );
11251
@@ -11170,11 +11258,11 @@
11258 ** using this routine to avoid a memory leak.
11259 **
11260 ** The [sqlite3_snapshot_free()] interface is only available when the
11261 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
11262 */
11263 SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*);
11264
11265 /*
11266 ** CAPI3REF: Compare the ages of two snapshot handles.
11267 ** METHOD: sqlite3_snapshot
11268 **
@@ -11197,11 +11285,11 @@
11285 ** snapshot, and a positive value if P1 is a newer snapshot than P2.
11286 **
11287 ** This interface is only available if SQLite is compiled with the
11288 ** [SQLITE_ENABLE_SNAPSHOT] option.
11289 */
11290 SQLITE_API int sqlite3_snapshot_cmp(
11291 sqlite3_snapshot *p1,
11292 sqlite3_snapshot *p2
11293 );
11294
11295 /*
@@ -11225,11 +11313,11 @@
11313 ** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
11314 **
11315 ** This interface is only available if SQLite is compiled with the
11316 ** [SQLITE_ENABLE_SNAPSHOT] option.
11317 */
11318 SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
11319
11320 /*
11321 ** CAPI3REF: Serialize a database
11322 **
11323 ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
@@ -11299,16 +11387,17 @@
11387 /*
11388 ** CAPI3REF: Deserialize a database
11389 **
11390 ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
11391 ** [database connection] D to disconnect from database S and then
11392 ** reopen S as an in-memory database based on the serialization
11393 ** contained in P. If S is a NULL pointer, the main database is
11394 ** used. The serialized database P is N bytes in size. M is the size
11395 ** of the buffer P, which might be larger than N. If M is larger than
11396 ** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then
11397 ** SQLite is permitted to add content to the in-memory database as
11398 ** long as the total size does not exceed M bytes.
11399 **
11400 ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
11401 ** invoke sqlite3_free() on the serialization buffer when the database
11402 ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
11403 ** SQLite will try to increase the buffer size using sqlite3_realloc64()
@@ -11371,10 +11460,56 @@
11460 */
11461 #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
11462 #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
11463 #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
11464
11465 /*
11466 ** CAPI3REF: Bind array values to the CARRAY table-valued function
11467 **
11468 ** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to
11469 ** one of the first argument of the [carray() table-valued function]. The
11470 ** S parameter is a pointer to the [prepared statement] that uses the carray()
11471 ** functions. I is the parameter index to be bound. P is a pointer to the
11472 ** array to be bound, and N is the number of eements in the array. The
11473 ** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
11474 ** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to
11475 ** indicate the datatype of the array being bound. The X argument is not a
11476 ** NULL pointer, then SQLite will invoke the function X on the P parameter
11477 ** after it has finished using P.
11478 */
11479 SQLITE_API SQLITE_API int sqlite3_carray_bind(
11480 sqlite3_stmt *pStmt, /* Statement to be bound */
11481 int i, /* Parameter index */
11482 void *aData, /* Pointer to array data */
11483 int nData, /* Number of data elements */
11484 int mFlags, /* CARRAY flags */
11485 void (*xDel)(void*) /* Destructor for aData */
11486 );
11487
11488 /*
11489 ** CAPI3REF: Datatypes for the CARRAY table-valued funtion
11490 **
11491 ** The fifth argument to the [sqlite3_carray_bind()] interface musts be
11492 ** one of the following constants, to specify the datatype of the array
11493 ** that is being bound into the [carray table-valued function].
11494 */
11495 #define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */
11496 #define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */
11497 #define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */
11498 #define SQLITE_CARRAY_TEXT 3 /* Data is char* */
11499 #define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */
11500
11501 /*
11502 ** Versions of the above #defines that omit the initial SQLITE_, for
11503 ** legacy compatibility.
11504 */
11505 #define CARRAY_INT32 0 /* Data is 32-bit signed integers */
11506 #define CARRAY_INT64 1 /* Data is 64-bit signed integers */
11507 #define CARRAY_DOUBLE 2 /* Data is doubles */
11508 #define CARRAY_TEXT 3 /* Data is char* */
11509 #define CARRAY_BLOB 4 /* Data is struct iovec */
11510
11511 /*
11512 ** Undo the hack that converts floating point types to integer for
11513 ** builds on processors without floating point support.
11514 */
11515 #ifdef SQLITE_OMIT_FLOATING_POINT
@@ -12629,10 +12764,19 @@
12764 ** CAPI3REF: Apply A Changeset To A Database
12765 **
12766 ** Apply a changeset or patchset to a database. These functions attempt to
12767 ** update the "main" database attached to handle db with the changes found in
12768 ** the changeset passed via the second and third arguments.
12769 **
12770 ** All changes made by these functions are enclosed in a savepoint transaction.
12771 ** If any other error (aside from a constraint failure when attempting to
12772 ** write to the target database) occurs, then the savepoint transaction is
12773 ** rolled back, restoring the target database to its original state, and an
12774 ** SQLite error code returned. Additionally, starting with version 3.51.0,
12775 ** an error code and error message that may be accessed using the
12776 ** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database
12777 ** handle.
12778 **
12779 ** The fourth argument (xFilter) passed to these functions is the "filter
12780 ** callback". This may be passed NULL, in which case all changes in the
12781 ** changeset are applied to the database. For sqlite3changeset_apply() and
12782 ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
@@ -12767,16 +12911,10 @@
12911 ** It is safe to execute SQL statements, including those that write to the
12912 ** table that the callback related to, from within the xConflict callback.
12913 ** This can be used to further customize the application's conflict
12914 ** resolution strategy.
12915 **
 
 
 
 
 
 
12916 ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
12917 ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
12918 ** may set (*ppRebase) to point to a "rebase" that may be used with the
12919 ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
12920 ** is set to the size of the buffer in bytes. It is the responsibility of the
@@ -14351,11 +14489,11 @@
14489
14490 /*
14491 ** Maximum number of pages in one database file.
14492 **
14493 ** This is really just the default value for the max_page_count pragma.
14494 ** This value can be lowered (or raised) at run-time using the
14495 ** max_page_count macro.
14496 */
14497 #ifndef SQLITE_MAX_PAGE_COUNT
14498 # define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
14499 #endif
@@ -15947,12 +16085,12 @@
16085 **
16086 ** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application
16087 ** must provide its own VFS implementation together with sqlite3_os_init()
16088 ** and sqlite3_os_end() routines.
16089 */
16090 #if SQLITE_OS_KV+1<=1 && SQLITE_OS_OTHER+1<=1 && \
16091 SQLITE_OS_WIN+1<=1 && SQLITE_OS_UNIX+1<=1
16092 # if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
16093 defined(__MINGW32__) || defined(__BORLANDC__)
16094 # define SQLITE_OS_WIN 1
16095 # define SQLITE_OS_UNIX 0
16096 # else
@@ -17450,10 +17588,13 @@
17588 #ifndef SQLITE_OMIT_TRACE
17589 SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
17590 #endif
17591 SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
17592 SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*);
17593 #ifdef SQLITE_ENABLE_PERCENTILE
17594 SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context*);
17595 #endif
17596
17597 SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*);
17598 SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
17599 SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
17600 SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
@@ -18122,11 +18263,11 @@
18263 struct sqlite3InitInfo { /* Information used during initialization */
18264 Pgno newTnum; /* Rootpage of table being initialized */
18265 u8 iDb; /* Which db file is being initialized */
18266 u8 busy; /* TRUE if currently initializing */
18267 unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
18268 unsigned imposterTable : 2; /* Building an imposter table */
18269 unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
18270 const char **azInit; /* "type", "name", and "tbl_name" columns */
18271 } init;
18272 int nVdbeActive; /* Number of VDBEs currently running */
18273 int nVdbeRead; /* Number of active VDBEs that read or write */
@@ -18205,10 +18346,11 @@
18346 int nStatement; /* Number of nested statement-transactions */
18347 i64 nDeferredCons; /* Net deferred constraints this transaction. */
18348 i64 nDeferredImmCons; /* Net deferred immediate constraints */
18349 int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
18350 DbClientData *pDbData; /* sqlite3_set_clientdata() content */
18351 u64 nSpill; /* TEMP content spilled to disk */
18352 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
18353 /* The following variables are all protected by the STATIC_MAIN
18354 ** mutex, not by sqlite3.mutex. They are used by code in notify.c.
18355 **
18356 ** When X.pUnlockConnection==Y, that means that X is waiting for Y to
@@ -18915,10 +19057,11 @@
19057 #define TF_Shadow 0x00001000 /* True for a shadow table */
19058 #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */
19059 #define TF_Ephemeral 0x00004000 /* An ephemeral table */
19060 #define TF_Eponymous 0x00008000 /* An eponymous virtual table */
19061 #define TF_Strict 0x00010000 /* STRICT mode */
19062 #define TF_Imposter 0x00020000 /* An imposter table */
19063
19064 /*
19065 ** Allowed values for Table.eTabType
19066 */
19067 #define TABTYP_NORM 0 /* Ordinary table */
@@ -19503,10 +19646,11 @@
19646 AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
19647 union {
19648 Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
19649 ** for a column of an index on an expression */
19650 Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
19651 int nReg; /* TK_NULLS: Number of registers to NULL out */
19652 struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
19653 int iAddr; /* Subroutine entry address */
19654 int regReturn; /* Register used to hold return address */
19655 } sub;
19656 } y;
@@ -20080,10 +20224,11 @@
20224 #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
20225 #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
20226 #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
20227 #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
20228 #define SF_Correlated 0x20000000 /* True if references the outer context */
20229 #define SF_OnToWhere 0x40000000 /* One or more ON clauses moved to WHERE */
20230
20231 /* True if SrcItem X is a subquery that has SF_NestedFrom */
20232 #define IsNestedFrom(X) \
20233 ((X)->fg.isSubquery && \
20234 ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0)
@@ -20833,10 +20978,11 @@
20978 struct Table *pTab; /* Table of generated column */
20979 struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
20980 SrcItem *pSrcItem; /* A single FROM clause item */
20981 DbFixer *pFix; /* See sqlite3FixSelect() */
20982 Mem *aMem; /* See sqlite3BtreeCursorHint() */
20983 struct CheckOnCtx *pCheckOnCtx; /* See selectCheckOnClauses() */
20984 } u;
20985 };
20986
20987 /*
20988 ** The following structure contains information used by the sqliteFix...
@@ -21540,10 +21686,11 @@
21686 SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
21687 #endif
21688 SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
21689 SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
21690 SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
21691 SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int);
21692 SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
21693 SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
21694 SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
21695 #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
21696 #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -21636,16 +21783,20 @@
21783 SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
21784 SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
21785 SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
21786 SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
21787 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
21788 SQLITE_PRIVATE Module *sqlite3JsonVtabRegister(sqlite3*,const char*);
21789 #endif
21790 SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
21791 SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
21792 SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
21793 SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p);
21794
21795 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY)
21796 SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3*);
21797 #endif
21798
21799 #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
21800 SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
21801 #endif
21802
@@ -22628,10 +22779,13 @@
22779 #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
22780 "ENABLE_BATCH_ATOMIC_WRITE",
22781 #endif
22782 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
22783 "ENABLE_BYTECODE_VTAB",
22784 #endif
22785 #ifdef SQLITE_ENABLE_CARRAY
22786 "ENABLE_CARRAY",
22787 #endif
22788 #ifdef SQLITE_ENABLE_CEROD
22789 "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
22790 #endif
22791 #ifdef SQLITE_ENABLE_COLUMN_METADATA
@@ -22718,10 +22872,13 @@
22872 #ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
22873 "ENABLE_ORDERED_SET_AGGREGATES",
22874 #endif
22875 #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
22876 "ENABLE_OVERSIZE_CELL_CHECK",
22877 #endif
22878 #ifdef SQLITE_ENABLE_PERCENTILE
22879 "ENABLE_PERCENTILE",
22880 #endif
22881 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
22882 "ENABLE_PREUPDATE_HOOK",
22883 #endif
22884 #ifdef SQLITE_ENABLE_QPSG
@@ -24202,11 +24359,14 @@
24359 Mem oldipk; /* Memory cell holding "old" IPK value */
24360 Mem *aNew; /* Array of new.* values */
24361 Table *pTab; /* Schema object being updated */
24362 Index *pPk; /* PK index if pTab is WITHOUT ROWID */
24363 sqlite3_value **apDflt; /* Array of default values, if required */
24364 union {
24365 KeyInfo sKey;
24366 u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
24367 } uKey;
24368 };
24369
24370 /*
24371 ** An instance of this object is used to pass an vector of values into
24372 ** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -24366,13 +24526,15 @@
24526 SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
24527 SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
24528 #endif
24529
24530 #ifndef SQLITE_OMIT_FOREIGN_KEY
24531 SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe*);
24532 SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe*);
24533 #else
24534 # define sqlite3VdbeCheckFkImmediate(p) 0
24535 # define sqlite3VdbeCheckFkDeferred(p) 0
24536 #endif
24537
24538 #ifdef SQLITE_DEBUG
24539 SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
24540 SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
@@ -24577,27 +24739,29 @@
24739 }
24740
24741 /*
24742 ** Query status information for a single database connection
24743 */
24744 SQLITE_API int sqlite3_db_status64(
24745 sqlite3 *db, /* The database connection whose status is desired */
24746 int op, /* Status verb */
24747 sqlite3_int64 *pCurrent, /* Write current value here */
24748 sqlite3_int64 *pHighwtr, /* Write high-water mark here */
24749 int resetFlag /* Reset high-water mark if true */
24750 ){
24751 int rc = SQLITE_OK; /* Return code */
24752 #ifdef SQLITE_ENABLE_API_ARMOR
24753 if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwtr==0 ){
24754 return SQLITE_MISUSE_BKPT;
24755 }
24756 #endif
24757 sqlite3_mutex_enter(db->mutex);
24758 switch( op ){
24759 case SQLITE_DBSTATUS_LOOKASIDE_USED: {
24760 int H = 0;
24761 *pCurrent = sqlite3LookasideUsed(db, &H);
24762 *pHighwtr = H;
24763 if( resetFlag ){
24764 LookasideSlot *p = db->lookaside.pFree;
24765 if( p ){
24766 while( p->pNext ) p = p->pNext;
24767 p->pNext = db->lookaside.pInit;
@@ -24624,11 +24788,11 @@
24788 testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
24789 testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
24790 assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
24791 assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
24792 *pCurrent = 0;
24793 *pHighwtr = db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
24794 if( resetFlag ){
24795 db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
24796 }
24797 break;
24798 }
@@ -24638,11 +24802,11 @@
24802 ** by all pagers associated with the given database connection. The
24803 ** highwater mark is meaningless and is returned as zero.
24804 */
24805 case SQLITE_DBSTATUS_CACHE_USED_SHARED:
24806 case SQLITE_DBSTATUS_CACHE_USED: {
24807 sqlite3_int64 totalUsed = 0;
24808 int i;
24809 sqlite3BtreeEnterAll(db);
24810 for(i=0; i<db->nDb; i++){
24811 Btree *pBt = db->aDb[i].pBt;
24812 if( pBt ){
@@ -24654,22 +24818,22 @@
24818 totalUsed += nByte;
24819 }
24820 }
24821 sqlite3BtreeLeaveAll(db);
24822 *pCurrent = totalUsed;
24823 *pHighwtr = 0;
24824 break;
24825 }
24826
24827 /*
24828 ** *pCurrent gets an accurate estimate of the amount of memory used
24829 ** to store the schema for all databases (main, temp, and any ATTACHed
24830 ** databases. *pHighwtr is set to zero.
24831 */
24832 case SQLITE_DBSTATUS_SCHEMA_USED: {
24833 int i; /* Used to iterate through schemas */
24834 int nByte = 0; /* Used to accumulate return value */
24835
24836 sqlite3BtreeEnterAll(db);
24837 db->pnBytesFreed = &nByte;
24838 assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
24839 db->lookaside.pEnd = db->lookaside.pStart;
@@ -24699,19 +24863,19 @@
24863 }
24864 db->pnBytesFreed = 0;
24865 db->lookaside.pEnd = db->lookaside.pTrueEnd;
24866 sqlite3BtreeLeaveAll(db);
24867
24868 *pHighwtr = 0;
24869 *pCurrent = nByte;
24870 break;
24871 }
24872
24873 /*
24874 ** *pCurrent gets an accurate estimate of the amount of memory used
24875 ** to store all prepared statements.
24876 ** *pHighwtr is set to zero.
24877 */
24878 case SQLITE_DBSTATUS_STMT_USED: {
24879 struct Vdbe *pVdbe; /* Used to iterate through VMs */
24880 int nByte = 0; /* Used to accumulate return value */
24881
@@ -24722,19 +24886,19 @@
24886 sqlite3VdbeDelete(pVdbe);
24887 }
24888 db->lookaside.pEnd = db->lookaside.pTrueEnd;
24889 db->pnBytesFreed = 0;
24890
24891 *pHighwtr = 0; /* IMP: R-64479-57858 */
24892 *pCurrent = nByte;
24893
24894 break;
24895 }
24896
24897 /*
24898 ** Set *pCurrent to the total cache hits or misses encountered by all
24899 ** pagers the database handle is connected to. *pHighwtr is always set
24900 ** to zero.
24901 */
24902 case SQLITE_DBSTATUS_CACHE_SPILL:
24903 op = SQLITE_DBSTATUS_CACHE_WRITE+1;
24904 /* no break */ deliberate_fall_through
@@ -24750,23 +24914,43 @@
24914 if( db->aDb[i].pBt ){
24915 Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
24916 sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
24917 }
24918 }
24919 *pHighwtr = 0; /* IMP: R-42420-56072 */
24920 /* IMP: R-54100-20147 */
24921 /* IMP: R-29431-39229 */
24922 *pCurrent = nRet;
24923 break;
24924 }
24925
24926 /* Set *pCurrent to the number of bytes that the db database connection
24927 ** has spilled to the filesystem in temporary files that could have been
24928 ** stored in memory, had sufficient memory been available.
24929 ** The *pHighwater is always set to zero.
24930 */
24931 case SQLITE_DBSTATUS_TEMPBUF_SPILL: {
24932 u64 nRet = 0;
24933 if( db->aDb[1].pBt ){
24934 Pager *pPager = sqlite3BtreePager(db->aDb[1].pBt);
24935 sqlite3PagerCacheStat(pPager, SQLITE_DBSTATUS_CACHE_WRITE,
24936 resetFlag, &nRet);
24937 nRet *= sqlite3BtreeGetPageSize(db->aDb[1].pBt);
24938 }
24939 nRet += db->nSpill;
24940 if( resetFlag ) db->nSpill = 0;
24941 *pHighwtr = 0;
24942 *pCurrent = nRet;
24943 break;
24944 }
24945
24946 /* Set *pCurrent to non-zero if there are unresolved deferred foreign
24947 ** key constraints. Set *pCurrent to zero if all foreign key constraints
24948 ** have been satisfied. The *pHighwtr is always set to zero.
24949 */
24950 case SQLITE_DBSTATUS_DEFERRED_FKS: {
24951 *pHighwtr = 0; /* IMP: R-11967-56545 */
24952 *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
24953 break;
24954 }
24955
24956 default: {
@@ -24774,10 +24958,35 @@
24958 }
24959 }
24960 sqlite3_mutex_leave(db->mutex);
24961 return rc;
24962 }
24963
24964 /*
24965 ** 32-bit variant of sqlite3_db_status64()
24966 */
24967 SQLITE_API int sqlite3_db_status(
24968 sqlite3 *db, /* The database connection whose status is desired */
24969 int op, /* Status verb */
24970 int *pCurrent, /* Write current value here */
24971 int *pHighwtr, /* Write high-water mark here */
24972 int resetFlag /* Reset high-water mark if true */
24973 ){
24974 sqlite3_int64 C = 0, H = 0;
24975 int rc;
24976 #ifdef SQLITE_ENABLE_API_ARMOR
24977 if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwtr==0 ){
24978 return SQLITE_MISUSE_BKPT;
24979 }
24980 #endif
24981 rc = sqlite3_db_status64(db, op, &C, &H, resetFlag);
24982 if( rc==0 ){
24983 *pCurrent = C & 0x7fffffff;
24984 *pHighwtr = H & 0x7fffffff;
24985 }
24986 return rc;
24987 }
24988
24989 /************** End of status.c **********************************************/
24990 /************** Begin file date.c ********************************************/
24991 /*
24992 ** 2003 October 31
@@ -24967,10 +25176,14 @@
25176 if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
25177 return 1;
25178 }
25179 zDate += 5;
25180 p->tz = sgn*(nMn + nHr*60);
25181 if( p->tz==0 ){ /* Forum post 2025-09-17T10:12:14z */
25182 p->isLocal = 0;
25183 p->isUtc = 1;
25184 }
25185 zulu_time:
25186 while( sqlite3Isspace(*zDate) ){ zDate++; }
25187 return *zDate!=0;
25188 }
25189
@@ -26162,12 +26375,12 @@
26375 ** %j day of year 001-366
26376 ** %J ** julian day number
26377 ** %l hour 1-12 (leading zero converted to space)
26378 ** %m month 01-12
26379 ** %M minute 00-59
26380 ** %p "AM" or "PM"
26381 ** %P "am" or "pm"
26382 ** %R time as HH:MM
26383 ** %s seconds since 1970-01-01
26384 ** %S seconds 00-59
26385 ** %T time as HH:MM:SS
26386 ** %u day of week 1-7 Monday==1, Sunday==7
@@ -31770,56 +31983,74 @@
31983 etByte base; /* The base for radix conversion */
31984 etByte flags; /* One or more of FLAG_ constants below */
31985 etByte type; /* Conversion paradigm */
31986 etByte charset; /* Offset into aDigits[] of the digits string */
31987 etByte prefix; /* Offset into aPrefix[] of the prefix string */
31988 char iNxt; /* Next with same hash, or 0 for end of chain */
31989 } et_info;
31990
31991 /*
31992 ** Allowed values for et_info.flags
31993 */
31994 #define FLAG_SIGNED 1 /* True if the value to convert is signed */
31995 #define FLAG_STRING 4 /* Allow infinite precision */
31996
31997 /*
31998 ** The table is searched by hash. In the case of %C where C is the character
31999 ** and that character has ASCII value j, then the hash is j%23.
32000 **
32001 ** The order of the entries in fmtinfo[] and the hash chain was entered
32002 ** manually, but based on the output of the following TCL script:
32003 */
32004 #if 0 /***** Beginning of script ******/
32005 foreach c {d s g z q Q w c o u x X f e E G i n % p T S r} {
32006 scan $c %c x
32007 set n($c) $x
32008 }
32009 set mx [llength [array names n]]
32010 puts "count: $mx"
32011
32012 set mx 27
32013 puts "*********** mx=$mx ************"
32014 for {set r 0} {$r<$mx} {incr r} {
32015 puts -nonewline [format %2d: $r]
32016 foreach c [array names n] {
32017 if {($n($c))%$mx==$r} {puts -nonewline " $c"}
32018 }
32019 puts ""
32020 }
32021 #endif /***** End of script ********/
32022
32023 static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
32024 static const char aPrefix[] = "-x0\000X0";
32025 static const et_info fmtinfo[23] = {
32026 /* 0 */ { 's', 0, 4, etSTRING, 0, 0, 1 },
32027 /* 1 */ { 'E', 0, 1, etEXP, 14, 0, 0 }, /* Hash: 0 */
32028 /* 2 */ { 'u', 10, 0, etDECIMAL, 0, 0, 3 },
32029 /* 3 */ { 'G', 0, 1, etGENERIC, 14, 0, 0 }, /* Hash: 2 */
32030 /* 4 */ { 'w', 0, 4, etESCAPE_w, 0, 0, 0 },
32031 /* 5 */ { 'x', 16, 0, etRADIX, 16, 1, 0 },
32032 /* 6 */ { 'c', 0, 0, etCHARX, 0, 0, 0 }, /* Hash: 7 */
32033 /* 7 */ { 'z', 0, 4, etDYNSTRING, 0, 0, 6 },
32034 /* 8 */ { 'd', 10, 1, etDECIMAL, 0, 0, 0 },
32035 /* 9 */ { 'e', 0, 1, etEXP, 30, 0, 0 },
32036 /* 10 */ { 'f', 0, 1, etFLOAT, 0, 0, 0 },
32037 /* 11 */ { 'g', 0, 1, etGENERIC, 30, 0, 0 },
32038 /* 12 */ { 'Q', 0, 4, etESCAPE_Q, 0, 0, 0 },
32039 /* 13 */ { 'i', 10, 1, etDECIMAL, 0, 0, 0 },
32040 /* 14 */ { '%', 0, 0, etPERCENT, 0, 0, 16 },
32041 /* 15 */ { 'T', 0, 0, etTOKEN, 0, 0, 0 },
32042 /* 16 */ { 'S', 0, 0, etSRCITEM, 0, 0, 0 }, /* Hash: 14 */
32043 /* 17 */ { 'X', 16, 0, etRADIX, 0, 4, 0 }, /* Hash: 19 */
32044 /* 18 */ { 'n', 0, 0, etSIZE, 0, 0, 0 },
32045 /* 19 */ { 'o', 8, 0, etRADIX, 0, 2, 17 },
32046 /* 20 */ { 'p', 16, 0, etPOINTER, 0, 1, 0 },
32047 /* 21 */ { 'q', 0, 4, etESCAPE_q, 0, 0, 0 },
32048 /* 22 */ { 'r', 10, 1, etORDINAL, 0, 0, 0 }
 
 
 
 
32049 };
32050
32051 /* Additional Notes:
32052 **
32053 ** %S Takes a pointer to SrcItem. Shows name or database.name
32054 ** %!S Like %S but prefer the zName over the zAlias
32055 */
32056
@@ -31942,11 +32173,14 @@
32173 if( c!='%' ){
32174 bufpt = (char *)fmt;
32175 #if HAVE_STRCHRNUL
32176 fmt = strchrnul(fmt, '%');
32177 #else
32178 fmt = strchr(fmt, '%');
32179 if( fmt==0 ){
32180 fmt = bufpt + strlen(bufpt);
32181 }
32182 #endif
32183 sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
32184 if( *fmt==0 ) break;
32185 }
32186 if( (c=(*++fmt))==0 ){
@@ -32056,19 +32290,36 @@
32290 }
32291 }
32292 }while( !done && (c=(*++fmt))!=0 );
32293
32294 /* Fetch the info entry for the field */
32295 #ifdef SQLITE_EBCDIC
32296 /* The hash table only works for ASCII. For EBCDIC, we need to do
32297 ** a linear search of the table */
32298 infop = &fmtinfo[0];
32299 xtype = etINVALID;
32300 for(idx=0; idx<ArraySize(fmtinfo); idx++){
32301 if( c==fmtinfo[idx].fmttype ){
32302 infop = &fmtinfo[idx];
32303 xtype = infop->type;
32304 break;
32305 }
32306 }
32307 #else
32308 /* Fast hash-table lookup */
32309 assert( ArraySize(fmtinfo)==23 );
32310 idx = ((unsigned)c) % 23;
32311 if( fmtinfo[idx].fmttype==c
32312 || fmtinfo[idx = fmtinfo[idx].iNxt].fmttype==c
32313 ){
32314 infop = &fmtinfo[idx];
32315 xtype = infop->type;
32316 }else{
32317 infop = &fmtinfo[0];
32318 xtype = etINVALID;
32319 }
32320 #endif
32321
32322 /*
32323 ** At this point, variables are initialized as follows:
32324 **
32325 ** flag_alternateform TRUE if a '#' is present.
@@ -32252,11 +32503,25 @@
32503 length = sqlite3Strlen30(bufpt);
32504 break;
32505 }
32506 }
32507 if( s.sign=='-' ){
32508 if( flag_alternateform
32509 && !flag_prefix
32510 && xtype==etFLOAT
32511 && s.iDP<=iRound
32512 ){
32513 /* Suppress the minus sign if all of the following are true:
32514 ** * The value displayed is zero
32515 ** * The '#' flag is used
32516 ** * The '+' flag is not used, and
32517 ** * The format is %f
32518 */
32519 prefix = 0;
32520 }else{
32521 prefix = '-';
32522 }
32523 }else{
32524 prefix = flag_prefix;
32525 }
32526
32527 exp = s.iDP-1;
@@ -33463,13 +33728,17 @@
33728 sqlite3StrAccumFinish(&x);
33729 sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
33730 n = 0;
33731 if( pItem->fg.isSubquery ) n++;
33732 if( pItem->fg.isTabFunc ) n++;
33733 if( pItem->fg.isUsing || pItem->u3.pOn!=0 ) n++;
33734 if( pItem->fg.isUsing ){
33735 sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
33736 }else if( pItem->u3.pOn!=0 ){
33737 sqlite3TreeViewItem(pView, "ON", (--n)>0);
33738 sqlite3TreeViewExpr(pView, pItem->u3.pOn, 0);
33739 sqlite3TreeViewPop(&pView);
33740 }
33741 if( pItem->fg.isSubquery ){
33742 assert( n==1 );
33743 if( pItem->pSTab ){
33744 Table *pTab = pItem->pSTab;
@@ -38108,11 +38377,11 @@
38377 static int kvstorageDelete(const char*, const char *zKey);
38378 static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
38379 #define KVSTORAGE_KEY_SZ 32
38380
38381 /* Expand the key name with an appropriate prefix and put the result
38382 ** in zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
38383 ** KVSTORAGE_KEY_SZ bytes.
38384 */
38385 static void kvstorageMakeKey(
38386 const char *zClass,
38387 const char *zKeyIn,
@@ -38167,14 +38436,16 @@
38436 ** enough to hold it all. The value put into zBuf must always be zero
38437 ** terminated, even if it gets truncated because nBuf is not large enough.
38438 **
38439 ** Return the total number of bytes in the data, without truncation, and
38440 ** not counting the final zero terminator. Return -1 if the key does
38441 ** not exist or its key cannot be read.
38442 **
38443 ** If nBuf<=0 then this routine simply returns the size of the data
38444 ** without actually reading it. Similarly, if nBuf==1 then it
38445 ** zero-terminates zBuf at zBuf[0] and returns the size of the data
38446 ** without reading it.
38447 */
38448 static int kvstorageRead(
38449 const char *zClass,
38450 const char *zKey,
38451 char *zBuf,
@@ -38219,15 +38490,13 @@
38490 /*
38491 ** An internal level of indirection which enables us to replace the
38492 ** kvvfs i/o methods with JavaScript implementations in WASM builds.
38493 ** Maintenance reminder: if this struct changes in any way, the JSON
38494 ** rendering of its structure must be updated in
38495 ** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary
38496 ** compatibility concerns, so it does not need an iVersion
38497 ** member.
 
 
38498 */
38499 typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
38500 struct sqlite3_kvvfs_methods {
38501 int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
38502 int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
@@ -38240,12 +38509,12 @@
38509 ** for JavaScript-side implementations in WASM builds. In such builds
38510 ** it cannot be const, but in native builds it should be so that
38511 ** the compiler can hopefully optimize this level of indirection out.
38512 ** That said, kvvfs is intended primarily for use in WASM builds.
38513 **
38514 ** This is not explicitly flagged as static because the amalgamation
38515 ** build will tag it with SQLITE_PRIVATE.
38516 */
38517 #ifndef SQLITE_WASM
38518 const
38519 #endif
38520 SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
@@ -39414,14 +39683,15 @@
39683 #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
39684 aSyscall[13].pCurrent)
39685
39686 #if defined(HAVE_FCHMOD)
39687 { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
39688 #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
39689 #else
39690 { "fchmod", (sqlite3_syscall_ptr)0, 0 },
39691 #define osFchmod(FID,MODE) 0
39692 #endif
 
39693
39694 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
39695 { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
39696 #else
39697 { "fallocate", (sqlite3_syscall_ptr)0, 0 },
@@ -39675,13 +39945,12 @@
39945 return fd;
39946 }
39947
39948 /*
39949 ** Helper functions to obtain and relinquish the global mutex. The
39950 ** global mutex is used to protect the unixInodeInfo objects used by
39951 ** this file, all of which may be shared by multiple threads.
 
39952 **
39953 ** Function unixMutexHeld() is used to assert() that the global mutex
39954 ** is held when required. This function is only used as part of assert()
39955 ** statements. e.g.
39956 **
@@ -39879,10 +40148,11 @@
40148 /*
40149 ** All unique filenames are held on a linked list headed by this
40150 ** variable:
40151 */
40152 static struct vxworksFileId *vxworksFileList = 0;
40153 static sqlite3_mutex *vxworksMutex = 0;
40154
40155 /*
40156 ** Simplify a filename into its canonical form
40157 ** by making the following changes:
40158 **
@@ -39944,47 +40214,47 @@
40214
40215 /* Search for an existing entry that matching the canonical name.
40216 ** If found, increment the reference count and return a pointer to
40217 ** the existing file ID.
40218 */
40219 sqlite3_mutex_enter(vxworksMutex);
40220 for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){
40221 if( pCandidate->nName==n
40222 && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0
40223 ){
40224 sqlite3_free(pNew);
40225 pCandidate->nRef++;
40226 sqlite3_mutex_leave(vxworksMutex);
40227 return pCandidate;
40228 }
40229 }
40230
40231 /* No match was found. We will make a new file ID */
40232 pNew->nRef = 1;
40233 pNew->nName = n;
40234 pNew->pNext = vxworksFileList;
40235 vxworksFileList = pNew;
40236 sqlite3_mutex_leave(vxworksMutex);
40237 return pNew;
40238 }
40239
40240 /*
40241 ** Decrement the reference count on a vxworksFileId object. Free
40242 ** the object when the reference count reaches zero.
40243 */
40244 static void vxworksReleaseFileId(struct vxworksFileId *pId){
40245 sqlite3_mutex_enter(vxworksMutex);
40246 assert( pId->nRef>0 );
40247 pId->nRef--;
40248 if( pId->nRef==0 ){
40249 struct vxworksFileId **pp;
40250 for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){}
40251 assert( *pp==pId );
40252 *pp = pId->pNext;
40253 sqlite3_free(pId);
40254 }
40255 sqlite3_mutex_leave(vxworksMutex);
40256 }
40257 #endif /* OS_VXWORKS */
40258 /*************** End of Unique File ID Utility Used By VxWorks ****************
40259 ******************************************************************************/
40260
@@ -40368,10 +40638,14 @@
40638 do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
40639 if( rc!=1 ){
40640 storeLastErrno(pFile, errno);
40641 return SQLITE_IOERR;
40642 }
40643 if( fsync(fd) ){
40644 storeLastErrno(pFile, errno);
40645 return SQLITE_IOERR_FSYNC;
40646 }
40647 rc = osFstat(fd, &statbuf);
40648 if( rc!=0 ){
40649 storeLastErrno(pFile, errno);
40650 return SQLITE_IOERR;
40651 }
@@ -40537,22 +40811,46 @@
40811 static int osSetPosixAdvisoryLock(
40812 int h, /* The file descriptor on which to take the lock */
40813 struct flock *pLock, /* The description of the lock */
40814 unixFile *pFile /* Structure holding timeout value */
40815 ){
40816 int rc = 0;
40817
40818 if( pFile->iBusyTimeout==0 ){
40819 /* unixFile->iBusyTimeout is set to 0. In this case, attempt a
40820 ** non-blocking lock. */
40821 rc = osFcntl(h,F_SETLK,pLock);
40822 }else{
40823 /* unixFile->iBusyTimeout is set to greater than zero. In this case,
40824 ** attempt a blocking-lock with a unixFile->iBusyTimeout ms timeout.
40825 **
40826 ** On systems that support some kind of blocking file lock operation,
40827 ** this block should be replaced by code to attempt a blocking lock
40828 ** with a timeout of unixFile->iBusyTimeout ms. The code below is
40829 ** placeholder code. If SQLITE_TEST is defined, the placeholder code
40830 ** retries the lock once every 1ms until it succeeds or the timeout
40831 ** is reached. Or, if SQLITE_TEST is not defined, the placeholder
40832 ** code attempts a non-blocking lock and sets unixFile->iBusyTimeout
40833 ** to 0. This causes the caller to return SQLITE_BUSY, instead of
40834 ** SQLITE_BUSY_TIMEOUT to SQLite - as required by a VFS that does not
40835 ** support blocking locks.
40836 */
40837 #ifdef SQLITE_TEST
40838 int tm = pFile->iBusyTimeout;
40839 while( tm>0 ){
40840 rc = osFcntl(h,F_SETLK,pLock);
40841 if( rc==0 ) break;
40842 unixSleep(0,1000);
40843 tm--;
40844 }
40845 #else
40846 rc = osFcntl(h,F_SETLK,pLock);
40847 pFile->iBusyTimeout = 0;
40848 #endif
40849 /* End of code to replace with real blocking-locks code. */
40850 }
40851
40852 return rc;
40853 }
40854 #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
40855
40856
@@ -44869,14 +45167,21 @@
45167 #endif
45168
45169 storeLastErrno(pNew, 0);
45170 #if OS_VXWORKS
45171 if( rc!=SQLITE_OK ){
45172 if( h>=0 ){
45173 robust_close(pNew, h, __LINE__);
45174 h = -1;
45175 }
45176 if( pNew->ctrlFlags & UNIXFILE_DELETE ){
45177 osUnlink(zFilename);
45178 }
45179 if( pNew->pId ){
45180 vxworksReleaseFileId(pNew->pId);
45181 pNew->pId = 0;
45182 }
45183 }
45184 #endif
45185 if( rc!=SQLITE_OK ){
45186 if( h>=0 ) robust_close(pNew, h, __LINE__);
45187 }else{
@@ -44916,10 +45221,13 @@
45221 struct stat buf;
45222 const char *zDir = sqlite3_temp_directory;
45223
45224 while(1){
45225 if( zDir!=0
45226 #if OS_VXWORKS
45227 && zDir[0]=='/'
45228 #endif
45229 && osStat(zDir, &buf)==0
45230 && S_ISDIR(buf.st_mode)
45231 && osAccess(zDir, 03)==0
45232 ){
45233 return zDir;
@@ -45229,10 +45537,16 @@
45537 assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
45538 || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
45539 || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
45540 || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
45541 );
45542
45543 #if OS_VXWORKS
45544 /* The file-ID mechanism used in Vxworks requires that all pathnames
45545 ** provided to unixOpen must be absolute pathnames. */
45546 if( zPath!=0 && zPath[0]!='/' ){ return SQLITE_CANTOPEN; }
45547 #endif
45548
45549 /* Detect a pid change and reset the PRNG. There is a race condition
45550 ** here such that two or more threads all trying to open databases at
45551 ** the same instant might all reset the PRNG. But multiple resets
45552 ** are harmless.
@@ -45430,12 +45744,15 @@
45744 goto open_finished;
45745 }
45746 }
45747 #endif
45748
45749 assert( zPath==0
45750 || zPath[0]=='/'
45751 || eType==SQLITE_OPEN_SUPER_JOURNAL
45752 || eType==SQLITE_OPEN_MAIN_JOURNAL
45753 || eType==SQLITE_OPEN_TEMP_JOURNAL
45754 );
45755 rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
45756
45757 open_finished:
45758 if( rc!=SQLITE_OK ){
@@ -47160,10 +47477,13 @@
47477 }
47478 #ifdef SQLITE_OS_KV_OPTIONAL
47479 sqlite3KvvfsInit();
47480 #endif
47481 unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
47482 #if OS_VXWORKS
47483 vxworksMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS2);
47484 #endif
47485
47486 #ifndef SQLITE_OMIT_WAL
47487 /* Validate lock assumptions */
47488 assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */
47489 assert( UNIX_SHM_BASE==120 ); /* Start of locking area */
@@ -47194,10 +47514,13 @@
47514 ** to release dynamically allocated objects. But not on unix.
47515 ** This routine is a no-op for unix.
47516 */
47517 SQLITE_API int sqlite3_os_end(void){
47518 unixBigLock = 0;
47519 #if OS_VXWORKS
47520 vxworksMutex = 0;
47521 #endif
47522 return SQLITE_OK;
47523 }
47524
47525 #endif /* SQLITE_OS_UNIX */
47526
@@ -51192,204 +51515,10 @@
51515 ** on allocation size granularity boundaries.
51516 ** During sqlite3_os_init() we do a GetSystemInfo()
51517 ** to get the granularity size.
51518 */
51519 static SYSTEM_INFO winSysInfo;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51520
51521 /*
51522 ** Convert a UTF-8 filename into whatever form the underlying
51523 ** operating system wants filenames in. Space to hold the result
51524 ** is obtained from malloc and must be freed by the calling
@@ -51483,10 +51612,212 @@
51612 }
51613 #endif
51614 /* caller will handle out of memory */
51615 return zConverted;
51616 }
51617
51618 #ifndef SQLITE_OMIT_WAL
51619
51620 /*
51621 ** Helper functions to obtain and relinquish the global mutex. The
51622 ** global mutex is used to protect the winLockInfo objects used by
51623 ** this file, all of which may be shared by multiple threads.
51624 **
51625 ** Function winShmMutexHeld() is used to assert() that the global mutex
51626 ** is held when required. This function is only used as part of assert()
51627 ** statements. e.g.
51628 **
51629 ** winShmEnterMutex()
51630 ** assert( winShmMutexHeld() );
51631 ** winShmLeaveMutex()
51632 */
51633 static sqlite3_mutex *winBigLock = 0;
51634 static void winShmEnterMutex(void){
51635 sqlite3_mutex_enter(winBigLock);
51636 }
51637 static void winShmLeaveMutex(void){
51638 sqlite3_mutex_leave(winBigLock);
51639 }
51640 #ifndef NDEBUG
51641 static int winShmMutexHeld(void) {
51642 return sqlite3_mutex_held(winBigLock);
51643 }
51644 #endif
51645
51646 /*
51647 ** Object used to represent a single file opened and mmapped to provide
51648 ** shared memory. When multiple threads all reference the same
51649 ** log-summary, each thread has its own winFile object, but they all
51650 ** point to a single instance of this object. In other words, each
51651 ** log-summary is opened only once per process.
51652 **
51653 ** winShmMutexHeld() must be true when creating or destroying
51654 ** this object, or while editing the global linked list that starts
51655 ** at winShmNodeList.
51656 **
51657 ** When reading or writing the linked list starting at winShmNode.pWinShmList,
51658 ** pShmNode->mutex must be held.
51659 **
51660 ** The following fields are constant after the object is created:
51661 **
51662 ** zFilename
51663 ** hSharedShm
51664 ** mutex
51665 ** bUseSharedLockHandle
51666 **
51667 ** Either winShmNode.mutex must be held or winShmNode.pWinShmList==0 and
51668 ** winShmMutexHeld() is true when reading or writing any other field
51669 ** in this structure.
51670 **
51671 ** File-handle hSharedShm is always used to (a) take the DMS lock, (b)
51672 ** truncate the *-shm file if the DMS-locking protocol demands it, and
51673 ** (c) map regions of the *-shm file into memory using MapViewOfFile()
51674 ** or similar. If bUseSharedLockHandle is true, then other locks are also
51675 ** taken on hSharedShm. Or, if bUseSharedLockHandle is false, then other
51676 ** locks are taken using each connection's winShm.hShm handles.
51677 */
51678 struct winShmNode {
51679 sqlite3_mutex *mutex; /* Mutex to access this object */
51680 char *zFilename; /* Name of the file */
51681 HANDLE hSharedShm; /* File handle open on zFilename */
51682 int bUseSharedLockHandle; /* True to use hSharedShm for everything */
51683
51684 int isUnlocked; /* DMS lock has not yet been obtained */
51685 int isReadonly; /* True if read-only */
51686 int szRegion; /* Size of shared-memory regions */
51687 int nRegion; /* Size of array apRegion */
51688
51689 struct ShmRegion {
51690 HANDLE hMap; /* File handle from CreateFileMapping */
51691 void *pMap;
51692 } *aRegion;
51693 DWORD lastErrno; /* The Windows errno from the last I/O error */
51694
51695 winShm *pWinShmList; /* List of winShm objects with ptrs to this */
51696
51697 winShmNode *pNext; /* Next in list of all winShmNode objects */
51698 #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51699 u8 nextShmId; /* Next available winShm.id value */
51700 #endif
51701 };
51702
51703 /*
51704 ** A global array of all winShmNode objects.
51705 **
51706 ** The winShmMutexHeld() must be true while reading or writing this list.
51707 */
51708 static winShmNode *winShmNodeList = 0;
51709
51710 /*
51711 ** Structure used internally by this VFS to record the state of an
51712 ** open shared memory connection. There is one such structure for each
51713 ** winFile open on a wal mode database.
51714 */
51715 struct winShm {
51716 winShmNode *pShmNode; /* The underlying winShmNode object */
51717 u16 sharedMask; /* Mask of shared locks held */
51718 u16 exclMask; /* Mask of exclusive locks held */
51719 HANDLE hShm; /* File-handle on *-shm file. For locking. */
51720 int bReadonly; /* True if hShm is opened read-only */
51721 #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
51722 u8 id; /* Id of this connection with its winShmNode */
51723 #endif
51724 winShm *pWinShmNext; /* Next winShm object on same winShmNode */
51725 };
51726
51727 /*
51728 ** Constants used for locking
51729 */
51730 #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
51731 #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
51732
51733 /* Forward references to VFS methods */
51734 static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
51735 static int winDelete(sqlite3_vfs *,const char*,int);
51736
51737 /*
51738 ** Purge the winShmNodeList list of all entries with winShmNode.pWinShmList==0.
51739 **
51740 ** This is not a VFS shared-memory method; it is a utility function called
51741 ** by VFS shared-memory methods.
51742 */
51743 static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
51744 winShmNode **pp;
51745 winShmNode *p;
51746 assert( winShmMutexHeld() );
51747 OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
51748 osGetCurrentProcessId(), deleteFlag));
51749 pp = &winShmNodeList;
51750 while( (p = *pp)!=0 ){
51751 if( p->pWinShmList==0 ){
51752 int i;
51753 if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
51754 for(i=0; i<p->nRegion; i++){
51755 BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
51756 OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
51757 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51758 UNUSED_VARIABLE_VALUE(bRc);
51759 bRc = osCloseHandle(p->aRegion[i].hMap);
51760 OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
51761 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
51762 UNUSED_VARIABLE_VALUE(bRc);
51763 }
51764 winHandleClose(p->hSharedShm);
51765 if( deleteFlag ){
51766 SimulateIOErrorBenign(1);
51767 sqlite3BeginBenignMalloc();
51768 winDelete(pVfs, p->zFilename, 0);
51769 sqlite3EndBenignMalloc();
51770 SimulateIOErrorBenign(0);
51771 }
51772 *pp = p->pNext;
51773 sqlite3_free(p->aRegion);
51774 sqlite3_free(p);
51775 }else{
51776 pp = &p->pNext;
51777 }
51778 }
51779 }
51780
51781 /*
51782 ** The DMS lock has not yet been taken on the shm file associated with
51783 ** pShmNode. Take the lock. Truncate the *-shm file if required.
51784 ** Return SQLITE_OK if successful, or an SQLite error code otherwise.
51785 */
51786 static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
51787 HANDLE h = pShmNode->hSharedShm;
51788 int rc = SQLITE_OK;
51789
51790 assert( sqlite3_mutex_held(pShmNode->mutex) );
51791 rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
51792 if( rc==SQLITE_OK ){
51793 /* We have an EXCLUSIVE lock on the DMS byte. This means that this
51794 ** is the first process to open the file. Truncate it to zero bytes
51795 ** in this case. */
51796 if( pShmNode->isReadonly ){
51797 rc = SQLITE_READONLY_CANTINIT;
51798 }else{
51799 rc = winHandleTruncate(h, 0);
51800 }
51801
51802 /* Release the EXCLUSIVE lock acquired above. */
51803 winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
51804 }else if( (rc & 0xFF)==SQLITE_BUSY ){
51805 rc = SQLITE_OK;
51806 }
51807
51808 if( rc==SQLITE_OK ){
51809 /* Take a SHARED lock on the DMS byte. */
51810 rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
51811 if( rc==SQLITE_OK ){
51812 pShmNode->isUnlocked = 0;
51813 }
51814 }
51815
51816 return rc;
51817 }
51818
51819
51820 /*
51821 ** This function is used to open a handle on a *-shm file.
51822 **
51823 ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
@@ -51579,10 +51910,64 @@
51910 *pbReadonly = bReadonly;
51911 *ph = h;
51912 return rc;
51913 }
51914
51915 /*
51916 ** Close pDbFd's connection to shared-memory. Delete the underlying
51917 ** *-shm file if deleteFlag is true.
51918 */
51919 static int winCloseSharedMemory(winFile *pDbFd, int deleteFlag){
51920 winShm *p; /* The connection to be closed */
51921 winShm **pp; /* Iterator for pShmNode->pWinShmList */
51922 winShmNode *pShmNode; /* The underlying shared-memory file */
51923
51924 p = pDbFd->pShm;
51925 if( p==0 ) return SQLITE_OK;
51926 if( p->hShm!=INVALID_HANDLE_VALUE ){
51927 osCloseHandle(p->hShm);
51928 }
51929
51930 winShmEnterMutex();
51931 pShmNode = p->pShmNode;
51932
51933 /* Remove this connection from the winShmNode.pWinShmList list */
51934 sqlite3_mutex_enter(pShmNode->mutex);
51935 for(pp=&pShmNode->pWinShmList; *pp!=p; pp=&(*pp)->pWinShmNext){}
51936 *pp = p->pWinShmNext;
51937 sqlite3_mutex_leave(pShmNode->mutex);
51938
51939 winShmPurge(pDbFd->pVfs, deleteFlag);
51940 winShmLeaveMutex();
51941
51942 /* Free the connection p */
51943 sqlite3_free(p);
51944 pDbFd->pShm = 0;
51945 return SQLITE_OK;
51946 }
51947
51948 /*
51949 ** testfixture builds may set this global variable to true via a
51950 ** Tcl interface. This forces the VFS to use the locking normally
51951 ** only used for UNC paths for all files.
51952 */
51953 #ifdef SQLITE_TEST
51954 SQLITE_API int sqlite3_win_test_unc_locking = 0;
51955 #else
51956 # define sqlite3_win_test_unc_locking 0
51957 #endif
51958
51959 /*
51960 ** Return true if the string passed as the only argument is likely
51961 ** to be a UNC path. In other words, if it starts with "\\".
51962 */
51963 static int winIsUNCPath(const char *zFile){
51964 if( zFile[0]=='\\' && zFile[1]=='\\' ){
51965 return 1;
51966 }
51967 return sqlite3_win_test_unc_locking;
51968 }
51969
51970 /*
51971 ** Open the shared-memory area associated with database file pDbFd.
51972 */
51973 static int winOpenSharedMemory(winFile *pDbFd){
@@ -51605,19 +51990,14 @@
51990 return SQLITE_IOERR_NOMEM_BKPT;
51991 }
51992 pNew->zFilename = (char*)&pNew[1];
51993 pNew->hSharedShm = INVALID_HANDLE_VALUE;
51994 pNew->isUnlocked = 1;
51995 pNew->bUseSharedLockHandle = winIsUNCPath(pDbFd->zPath);
51996 sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
51997 sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
51998
 
 
 
 
 
 
51999 /* Look to see if there is an existing winShmNode that can be used.
52000 ** If no matching winShmNode currently exists, then create a new one. */
52001 winShmEnterMutex();
52002 for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
52003 /* TBD need to come up with better match here. Perhaps
@@ -51634,11 +52014,11 @@
52014 }
52015
52016 /* Open a file-handle to use for mappings, and for the DMS lock. */
52017 if( rc==SQLITE_OK ){
52018 HANDLE h = INVALID_HANDLE_VALUE;
52019 pShmNode->isReadonly = sqlite3_uri_boolean(pDbFd->zPath,"readonly_shm",0);
52020 rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
52021 pShmNode->hSharedShm = h;
52022 }
52023
52024 /* If successful, link the new winShmNode into the global list. If an
@@ -51656,24 +52036,39 @@
52036 }
52037
52038 /* If no error has occurred, link the winShm object to the winShmNode and
52039 ** the winShm to pDbFd. */
52040 if( rc==SQLITE_OK ){
52041 sqlite3_mutex_enter(pShmNode->mutex);
52042 p->pShmNode = pShmNode;
52043 p->pWinShmNext = pShmNode->pWinShmList;
52044 pShmNode->pWinShmList = p;
52045 #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
52046 p->id = pShmNode->nextShmId++;
52047 #endif
52048 pDbFd->pShm = p;
52049 sqlite3_mutex_leave(pShmNode->mutex);
52050 }else if( p ){
 
52051 sqlite3_free(p);
52052 }
52053
52054 assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
52055 winShmLeaveMutex();
52056 sqlite3_free(pNew);
52057
52058 /* Open a file-handle on the *-shm file for this connection. This file-handle
52059 ** is only used for locking. The mapping of the *-shm file is created using
52060 ** the shared file handle in winShmNode.hSharedShm. */
52061 if( rc==SQLITE_OK && pShmNode->bUseSharedLockHandle==0 ){
52062 p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
52063 rc = winHandleOpen(pShmNode->zFilename, &p->bReadonly, &p->hShm);
52064 if( rc!=SQLITE_OK ){
52065 assert( p->hShm==INVALID_HANDLE_VALUE );
52066 winCloseSharedMemory(pDbFd, 0);
52067 }
52068 }
52069
52070 return rc;
52071 }
52072
52073 /*
52074 ** Close a connection to shared-memory. Delete the underlying
@@ -51681,37 +52076,11 @@
52076 */
52077 static int winShmUnmap(
52078 sqlite3_file *fd, /* Database holding shared memory */
52079 int deleteFlag /* Delete after closing if true */
52080 ){
52081 return winCloseSharedMemory((winFile*)fd, deleteFlag);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52082 }
52083
52084 /*
52085 ** Change the lock state for a shared-memory segment.
52086 */
@@ -51776,29 +52145,75 @@
52145 );
52146 if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
52147 || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
52148 || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
52149 ){
52150 HANDLE h = p->hShm;
52151
52152 if( flags & SQLITE_SHM_UNLOCK ){
52153 /* Case (a) - unlock. */
52154
52155 assert( (p->exclMask & p->sharedMask)==0 );
52156 assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
52157 assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
52158
52159 assert( !(flags & SQLITE_SHM_SHARED) || n==1 );
52160 if( pShmNode->bUseSharedLockHandle ){
52161 h = pShmNode->hSharedShm;
52162 if( flags & SQLITE_SHM_SHARED ){
52163 winShm *pShm;
52164 sqlite3_mutex_enter(pShmNode->mutex);
52165 for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
52166 if( pShm!=p && (pShm->sharedMask & mask) ){
52167 /* Another connection within this process is also holding this
52168 ** SHARED lock. So do not actually release the OS lock. */
52169 h = INVALID_HANDLE_VALUE;
52170 break;
52171 }
52172 }
52173 sqlite3_mutex_leave(pShmNode->mutex);
52174 }
52175 }
52176
52177 if( h!=INVALID_HANDLE_VALUE ){
52178 rc = winHandleUnlock(h, ofst+WIN_SHM_BASE, n);
52179 }
52180
52181 /* If successful, also clear the bits in sharedMask/exclMask */
52182 if( rc==SQLITE_OK ){
52183 p->exclMask = (p->exclMask & ~mask);
52184 p->sharedMask = (p->sharedMask & ~mask);
52185 }
52186 }else{
52187 int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
52188 DWORD nMs = winFileBusyTimeout(pDbFd);
52189
52190 if( pShmNode->bUseSharedLockHandle ){
52191 winShm *pShm;
52192 h = pShmNode->hSharedShm;
52193 sqlite3_mutex_enter(pShmNode->mutex);
52194 for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
52195 if( bExcl ){
52196 if( (pShm->sharedMask|pShm->exclMask) & mask ){
52197 rc = SQLITE_BUSY;
52198 h = INVALID_HANDLE_VALUE;
52199 }
52200 }else{
52201 if( pShm->sharedMask & mask ){
52202 h = INVALID_HANDLE_VALUE;
52203 }else if( pShm->exclMask & mask ){
52204 rc = SQLITE_BUSY;
52205 h = INVALID_HANDLE_VALUE;
52206 }
52207 }
52208 }
52209 sqlite3_mutex_leave(pShmNode->mutex);
52210 }
52211
52212 if( h!=INVALID_HANDLE_VALUE ){
52213 rc = winHandleLockTimeout(h, ofst+WIN_SHM_BASE, n, bExcl, nMs);
52214 }
52215 if( rc==SQLITE_OK ){
52216 if( bExcl ){
52217 p->exclMask = (p->exclMask | mask);
52218 }else{
52219 p->sharedMask = (p->sharedMask | mask);
@@ -61825,18 +62240,31 @@
62240 SQLITE_PRIVATE void sqlite3PagerSetFlags(
62241 Pager *pPager, /* The pager to set safety level for */
62242 unsigned pgFlags /* Various flags */
62243 ){
62244 unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
62245 if( pPager->tempFile || level==PAGER_SYNCHRONOUS_OFF ){
62246 pPager->noSync = 1;
62247 pPager->fullSync = 0;
62248 pPager->extraSync = 0;
62249 }else{
62250 pPager->noSync = 0;
62251 pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
62252
62253 /* Set Pager.extraSync if "PRAGMA synchronous=EXTRA" is requested, or
62254 ** if the file-system supports F2FS style atomic writes. If this flag
62255 ** is set, SQLite syncs the directory to disk immediately after deleting
62256 ** a journal file in "PRAGMA journal_mode=DELETE" mode. */
62257 if( level==PAGER_SYNCHRONOUS_EXTRA
62258 #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
62259 || (sqlite3OsDeviceCharacteristics(pPager->fd) & SQLITE_IOCAP_BATCH_ATOMIC)
62260 #endif
62261 ){
62262 pPager->extraSync = 1;
62263 }else{
62264 pPager->extraSync = 0;
62265 }
62266 }
62267 if( pPager->noSync ){
62268 pPager->syncFlags = 0;
62269 }else if( pgFlags & PAGER_FULLFSYNC ){
62270 pPager->syncFlags = SQLITE_SYNC_FULL;
@@ -65725,11 +66153,11 @@
66153 */
66154 sqlite3_exec(db, "PRAGMA table_list",0,0,0);
66155 }
66156 if( pPager->pWal ){
66157 rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
66158 (eMode<=SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
66159 pPager->pBusyHandlerArg,
66160 pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
66161 pnLog, pnCkpt
66162 );
66163 }
@@ -70330,11 +70758,12 @@
70758 assert( pWal->ckptLock==0 );
70759 assert( pWal->writeLock==0 );
70760
70761 /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
70762 ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
70763 assert( SQLITE_CHECKPOINT_NOOP<SQLITE_CHECKPOINT_PASSIVE );
70764 assert( eMode>SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
70765
70766 if( pWal->readOnly ) return SQLITE_READONLY;
70767 WALTRACE(("WAL%p: checkpoint begins\n", pWal));
70768
70769 /* Enable blocking locks, if possible. */
@@ -70347,35 +70776,39 @@
70776 ** checkpoint operation at the same time, the lock cannot be obtained and
70777 ** SQLITE_BUSY is returned.
70778 ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
70779 ** it will not be invoked in this case.
70780 */
70781 if( eMode!=SQLITE_CHECKPOINT_NOOP ){
70782 rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
70783 testcase( rc==SQLITE_BUSY );
70784 testcase( rc!=SQLITE_OK && xBusy2!=0 );
70785 if( rc==SQLITE_OK ){
70786 pWal->ckptLock = 1;
70787
70788 /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART
70789 ** and TRUNCATE modes also obtain the exclusive "writer" lock on the
70790 ** database file.
70791 **
70792 ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
70793 ** immediately, and a busy-handler is configured, it is invoked and the
70794 ** writer lock retried until either the busy-handler returns 0 or the
70795 ** lock is successfully obtained.
70796 */
70797 if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
70798 rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
70799 if( rc==SQLITE_OK ){
70800 pWal->writeLock = 1;
70801 }else if( rc==SQLITE_BUSY ){
70802 eMode2 = SQLITE_CHECKPOINT_PASSIVE;
70803 xBusy2 = 0;
70804 rc = SQLITE_OK;
70805 }
70806 }
70807 }
70808 }else{
70809 rc = SQLITE_OK;
70810 }
70811
70812
70813 /* Read the wal-index header. */
70814 SEH_TRY {
@@ -70385,21 +70818,21 @@
70818 ** or invoke the busy handler. The only lock such a checkpoint may
70819 ** attempt to obtain is a lock on a read-slot, and it should give up
70820 ** immediately and do a partial checkpoint if it cannot obtain it. */
70821 walDisableBlocking(pWal);
70822 rc = walIndexReadHdr(pWal, &isChanged);
70823 if( eMode2>SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
70824 if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
70825 sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
70826 }
70827 }
70828
70829 /* Copy data from the log to the database file. */
70830 if( rc==SQLITE_OK ){
70831 if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
70832 rc = SQLITE_CORRUPT_BKPT;
70833 }else if( eMode2!=SQLITE_CHECKPOINT_NOOP ){
70834 rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
70835 }
70836
70837 /* If no error occurred, set the output variables. */
70838 if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
@@ -89078,14 +89511,16 @@
89511 ** simple case then too.
89512 */
89513 if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
89514 || nTrans<=1
89515 ){
89516 if( needXcommit ){
89517 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89518 Btree *pBt = db->aDb[i].pBt;
89519 if( sqlite3BtreeTxnState(pBt)>=SQLITE_TXN_WRITE ){
89520 rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
89521 }
89522 }
89523 }
89524
89525 /* Do the commit only if all databases successfully complete phase 1.
89526 ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
@@ -89092,11 +89527,13 @@
89527 ** IO error while deleting or truncating a journal file. It is unlikely,
89528 ** but could happen. In this case abandon processing and return the error.
89529 */
89530 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89531 Btree *pBt = db->aDb[i].pBt;
89532 int txn = sqlite3BtreeTxnState(pBt);
89533 if( txn!=SQLITE_TXN_NONE ){
89534 assert( needXcommit || txn==SQLITE_TXN_READ );
89535 rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
89536 }
89537 }
89538 if( rc==SQLITE_OK ){
89539 sqlite3VtabCommit(db);
@@ -89347,32 +89784,35 @@
89784 return SQLITE_OK;
89785 }
89786
89787
89788 /*
89789 ** These functions are called when a transaction opened by the database
89790 ** handle associated with the VM passed as an argument is about to be
89791 ** committed. If there are outstanding foreign key constraint violations
89792 ** return an error code. Otherwise, SQLITE_OK.
89793 **
89794 ** If there are outstanding FK violations and this function returns
89795 ** non-zero, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89796 ** and write an error message to it.
89797 */
89798 #ifndef SQLITE_OMIT_FOREIGN_KEY
89799 static SQLITE_NOINLINE int vdbeFkError(Vdbe *p){
89800 p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89801 p->errorAction = OE_Abort;
89802 sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89803 if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89804 return SQLITE_CONSTRAINT_FOREIGNKEY;
89805 }
89806 SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe *p){
89807 if( p->nFkConstraint==0 ) return SQLITE_OK;
89808 return vdbeFkError(p);
89809 }
89810 SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe *p){
89811 sqlite3 *db = p->db;
89812 if( (db->nDeferredCons+db->nDeferredImmCons)==0 ) return SQLITE_OK;
89813 return vdbeFkError(p);
 
 
 
 
 
 
 
 
89814 }
89815 #endif
89816
89817 /*
89818 ** This routine is called the when a VDBE tries to halt. If the VDBE
@@ -89462,11 +89902,11 @@
89902 }
89903 }
89904
89905 /* Check for immediate foreign key violations. */
89906 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89907 (void)sqlite3VdbeCheckFkImmediate(p);
89908 }
89909
89910 /* If the auto-commit flag is set and this is the only active writer
89911 ** VM, then we do either a commit or rollback of the current transaction.
89912 **
@@ -89476,11 +89916,11 @@
89916 if( !sqlite3VtabInSync(db)
89917 && db->autoCommit
89918 && db->nVdbeWrite==(p->readOnly==0)
89919 ){
89920 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89921 rc = sqlite3VdbeCheckFkDeferred(p);
89922 if( rc!=SQLITE_OK ){
89923 if( NEVER(p->readOnly) ){
89924 sqlite3VdbeLeave(p);
89925 return SQLITE_ERROR;
89926 }
@@ -90341,19 +90781,19 @@
90781 /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
90782 pMem->szMalloc = 0;
90783 pMem->z = 0;
90784 sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
90785 d += sqlite3VdbeSerialTypeLen(serial_type);
 
90786 if( (++u)>=p->nField ) break;
90787 pMem++;
90788 }
90789 if( d>(u32)nKey && u ){
90790 assert( CORRUPT_DB );
90791 /* In a corrupt record entry, the last pMem might have been set up using
90792 ** uninitialized memory. Overwrite its value with NULL, to prevent
90793 ** warnings from MSAN. */
90794 sqlite3VdbeMemSetNull(pMem-(u<p->nField));
90795 }
90796 testcase( u == pKeyInfo->nKeyField + 1 );
90797 testcase( u < pKeyInfo->nKeyField + 1 );
90798 assert( u<=pKeyInfo->nKeyField + 1 );
90799 p->nField = u;
@@ -90520,10 +90960,36 @@
90960 ** Both *pMem1 and *pMem2 contain string values. Compare the two values
90961 ** using the collation sequence pColl. As usual, return a negative , zero
90962 ** or positive value if *pMem1 is less than, equal to or greater than
90963 ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
90964 */
90965 static SQLITE_NOINLINE int vdbeCompareMemStringWithEncodingChange(
90966 const Mem *pMem1,
90967 const Mem *pMem2,
90968 const CollSeq *pColl,
90969 u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
90970 ){
90971 int rc;
90972 const void *v1, *v2;
90973 Mem c1;
90974 Mem c2;
90975 sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90976 sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90977 sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90978 sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90979 v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90980 v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90981 if( (v1==0 || v2==0) ){
90982 if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90983 rc = 0;
90984 }else{
90985 rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90986 }
90987 sqlite3VdbeMemReleaseMalloc(&c1);
90988 sqlite3VdbeMemReleaseMalloc(&c2);
90989 return rc;
90990 }
90991 static int vdbeCompareMemString(
90992 const Mem *pMem1,
90993 const Mem *pMem2,
90994 const CollSeq *pColl,
90995 u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
@@ -90531,29 +90997,11 @@
90997 if( pMem1->enc==pColl->enc ){
90998 /* The strings are already in the correct encoding. Call the
90999 ** comparison function directly */
91000 return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
91001 }else{
91002 return vdbeCompareMemStringWithEncodingChange(pMem1,pMem2,pColl,prcErr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91003 }
91004 }
91005
91006 /*
91007 ** The input pBlob is guaranteed to be a Blob that is not marked
@@ -91607,11 +92055,11 @@
92055
92056 preupdate.v = v;
92057 preupdate.pCsr = pCsr;
92058 preupdate.op = op;
92059 preupdate.iNewReg = iReg;
92060 preupdate.pKeyinfo = &preupdate.uKey.sKey;
92061 preupdate.pKeyinfo->db = db;
92062 preupdate.pKeyinfo->enc = ENC(db);
92063 preupdate.pKeyinfo->nKeyField = pTab->nCol;
92064 preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
92065 preupdate.iKey1 = iKey1;
@@ -91641,10 +92089,21 @@
92089 sqlite3DbFree(db, preupdate.apDflt);
92090 }
92091 }
92092 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
92093
92094 #ifdef SQLITE_ENABLE_PERCENTILE
92095 /*
92096 ** Return the name of an SQL function associated with the sqlite3_context.
92097 */
92098 SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context *pCtx){
92099 assert( pCtx!=0 );
92100 assert( pCtx->pFunc!=0 );
92101 return pCtx->pFunc->zName;
92102 }
92103 #endif /* SQLITE_ENABLE_PERCENTILE */
92104
92105 /************** End of vdbeaux.c *********************************************/
92106 /************** Begin file vdbeapi.c *****************************************/
92107 /*
92108 ** 2004 May 26
92109 **
@@ -93338,12 +93797,16 @@
93797 if( rc==SQLITE_OK ){
93798 assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
93799 if( zData!=0 ){
93800 pVar = &p->aVar[i-1];
93801 rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
93802 if( rc==SQLITE_OK ){
93803 if( encoding==0 ){
93804 pVar->enc = ENC(p->db);
93805 }else{
93806 rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
93807 }
93808 }
93809 if( rc ){
93810 sqlite3Error(p->db, rc);
93811 rc = sqlite3ApiExit(p->db, rc);
93812 }
@@ -95247,11 +95710,11 @@
95710 ** to run faster.
95711 */
95712 static SQLITE_NOINLINE int vdbeColumnFromOverflow(
95713 VdbeCursor *pC, /* The BTree cursor from which we are reading */
95714 int iCol, /* The column to read */
95715 u32 t, /* The serial-type code for the column value */
95716 i64 iOffset, /* Offset to the start of the content value */
95717 u32 cacheStatus, /* Current Vdbe.cacheCtr value */
95718 u32 colCacheCtr, /* Current value of the column cache counter */
95719 Mem *pDest /* Store the value into this register. */
95720 ){
@@ -96256,11 +96719,11 @@
96719 ** exits. This opcode is used to raise foreign key constraint errors prior
96720 ** to returning results such as a row change count or the result of a
96721 ** RETURNING clause.
96722 */
96723 case OP_FkCheck: {
96724 if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){
96725 goto abort_due_to_error;
96726 }
96727 break;
96728 }
96729
@@ -96348,11 +96811,12 @@
96811 flags2 = pIn2->flags & ~MEM_Str;
96812 }else if( (flags2 & MEM_Zero)!=0 ){
96813 if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
96814 flags2 = pIn2->flags & ~MEM_Str;
96815 }
96816 nByte = pIn1->n;
96817 nByte += pIn2->n;
96818 if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
96819 goto too_big;
96820 }
96821 if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
96822 goto no_mem;
@@ -98173,11 +98637,11 @@
98637 assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
98638 assert( pRec->n>=0 );
98639 len = (u32)pRec->n;
98640 serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
98641 if( pRec->flags & MEM_Zero ){
98642 serial_type += (u32)pRec->u.nZero*2;
98643 if( nData ){
98644 if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
98645 len += pRec->u.nZero;
98646 }else{
98647 nZero += pRec->u.nZero;
@@ -98440,11 +98904,11 @@
98904 ** and this is a RELEASE command, then the current transaction
98905 ** is committed.
98906 */
98907 int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
98908 if( isTransaction && p1==SAVEPOINT_RELEASE ){
98909 if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
98910 goto vdbe_return;
98911 }
98912 db->autoCommit = 1;
98913 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
98914 p->pc = (int)(pOp - aOp);
@@ -98558,11 +99022,11 @@
99022 */
99023 sqlite3VdbeError(p, "cannot commit transaction - "
99024 "SQL statements in progress");
99025 rc = SQLITE_BUSY;
99026 goto abort_due_to_error;
99027 }else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
99028 goto vdbe_return;
99029 }else{
99030 db->autoCommit = (u8)desiredAutoCommit;
99031 }
99032 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
@@ -102490,10 +102954,11 @@
102954 aRes[1] = aRes[2] = -1;
102955 assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
102956 || pOp->p2==SQLITE_CHECKPOINT_FULL
102957 || pOp->p2==SQLITE_CHECKPOINT_RESTART
102958 || pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
102959 || pOp->p2==SQLITE_CHECKPOINT_NOOP
102960 );
102961 rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
102962 if( rc ){
102963 if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
102964 rc = SQLITE_OK;
@@ -104689,10 +105154,11 @@
105154 UnpackedRecord *pUnpacked; /* Space to unpack a record */
105155 SorterList list; /* List for thread to write to a PMA */
105156 SorterCompare xCompare; /* Compare function to use */
105157 SorterFile file; /* Temp file for level-0 PMAs */
105158 SorterFile file2; /* Space for other PMAs */
105159 u64 nSpill; /* Total bytes written by this task */
105160 };
105161
105162
105163 /*
105164 ** Main sorter structure. A single instance of this is allocated for each
@@ -104809,10 +105275,11 @@
105275 int nBuffer; /* Size of write buffer in bytes */
105276 int iBufStart; /* First byte of buffer to write */
105277 int iBufEnd; /* Last byte of buffer to write */
105278 i64 iWriteOff; /* Offset of start of buffer in file */
105279 sqlite3_file *pFd; /* File handle to write to */
105280 u64 nPmaSpill; /* Total number of bytes written */
105281 };
105282
105283 /*
105284 ** This object is the header on a single record while that record is being
105285 ** held in memory and prior to being written out as part of a PMA.
@@ -105667,10 +106134,16 @@
106134 SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
106135 VdbeSorter *pSorter;
106136 assert( pCsr->eCurType==CURTYPE_SORTER );
106137 pSorter = pCsr->uc.pSorter;
106138 if( pSorter ){
106139 /* Increment db->nSpill by the total number of bytes of data written
106140 ** to temp files by this sort operation. */
106141 int ii;
106142 for(ii=0; ii<pSorter->nTask; ii++){
106143 db->nSpill += pSorter->aTask[ii].nSpill;
106144 }
106145 sqlite3VdbeSorterReset(db, pSorter);
106146 sqlite3_free(pSorter->list.aMemory);
106147 sqlite3DbFree(db, pSorter);
106148 pCsr->uc.pSorter = 0;
106149 }
@@ -105892,10 +106365,11 @@
106365 if( p->iBufEnd==p->nBuffer ){
106366 p->eFWErr = sqlite3OsWrite(p->pFd,
106367 &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
106368 p->iWriteOff + p->iBufStart
106369 );
106370 p->nPmaSpill += (p->iBufEnd - p->iBufStart);
106371 p->iBufStart = p->iBufEnd = 0;
106372 p->iWriteOff += p->nBuffer;
106373 }
106374 assert( p->iBufEnd<p->nBuffer );
106375
@@ -105908,21 +106382,24 @@
106382 ** The results of using the PMA-writer after this call are undefined.
106383 ** Return SQLITE_OK if flushing the buffered data succeeds or is not
106384 ** required. Otherwise, return an SQLite error code.
106385 **
106386 ** Before returning, set *piEof to the offset immediately following the
106387 ** last byte written to the file. Also, increment (*pnSpill) by the total
106388 ** number of bytes written to the file.
106389 */
106390 static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof, u64 *pnSpill){
106391 int rc;
106392 if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
106393 p->eFWErr = sqlite3OsWrite(p->pFd,
106394 &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
106395 p->iWriteOff + p->iBufStart
106396 );
106397 p->nPmaSpill += (p->iBufEnd - p->iBufStart);
106398 }
106399 *piEof = (p->iWriteOff + p->iBufEnd);
106400 *pnSpill += p->nPmaSpill;
106401 sqlite3_free(p->aBuffer);
106402 rc = p->eFWErr;
106403 memset(p, 0, sizeof(PmaWriter));
106404 return rc;
106405 }
@@ -105998,11 +106475,11 @@
106475 vdbePmaWriteVarint(&writer, p->nVal);
106476 vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal);
106477 if( pList->aMemory==0 ) sqlite3_free(p);
106478 }
106479 pList->pList = p;
106480 rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof, &pTask->nSpill);
106481 }
106482
106483 vdbeSorterWorkDebug(pTask, "exit");
106484 assert( rc!=SQLITE_OK || pList->pList==0 );
106485 assert( rc!=SQLITE_OK || pTask->file.iEof==iSz );
@@ -106312,11 +106789,11 @@
106789 vdbePmaWriteBlob(&writer, pReader->aKey, nKey);
106790 assert( pIncr->pMerger->pTask==pTask );
106791 rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy);
106792 }
106793
106794 rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof, &pTask->nSpill);
106795 if( rc==SQLITE_OK ) rc = rc2;
106796 vdbeSorterPopulateDebug(pTask, "exit");
106797 return rc;
106798 }
106799
@@ -109256,12 +109733,12 @@
109733 assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
109734 if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
109735
109736 /*
109737 ** Expression p should encode a floating point value between 1.0 and 0.0.
109738 ** Return 134,217,728 (2^27) times this value. Or return -1 if p is not
109739 ** a floating point value between 1.0 and 0.0.
109740 */
109741 static int exprProbability(Expr *p){
109742 double r = -1.0;
109743 if( p->op!=TK_FLOAT ) return -1;
109744 assert( !ExprHasProperty(p, EP_IntValue) );
@@ -110613,18 +111090,21 @@
111090 ExprList *pList /* Expression list to resolve. May be NULL. */
111091 ){
111092 SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
111093 NameContext sNC; /* Name context for pParse->pNewTable */
111094 int rc;
111095 union {
111096 SrcList sSrc;
111097 u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
111098 } uSrc;
111099
111100 assert( type==0 || pTab!=0 );
111101 assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
111102 || type==NC_GenCol || pTab==0 );
111103 memset(&sNC, 0, sizeof(sNC));
111104 memset(&uSrc, 0, sizeof(uSrc));
111105 pSrc = &uSrc.sSrc;
111106 if( pTab ){
111107 pSrc->nSrc = 1;
111108 pSrc->a[0].zName = pTab->zName;
111109 pSrc->a[0].pSTab = pTab;
111110 pSrc->a[0].iCursor = -1;
@@ -111883,10 +112363,15 @@
112363 if( IsWindowFunc(pExpr) ){
112364 sqlite3ExprOrderByAggregateError(pParse, pExpr);
112365 sqlite3ExprListDelete(db, pOrderBy);
112366 return;
112367 }
112368 if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
112369 sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
112370 sqlite3ExprListDelete(db, pOrderBy);
112371 return;
112372 }
112373
112374 pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
112375 if( pOB==0 ){
112376 sqlite3ExprListDelete(db, pOrderBy);
112377 return;
@@ -113079,13 +113564,12 @@
113564 }
113565 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
113566 if( addrIsNull==0 ){
113567 /*
113568 ** If the right operand contains a subquery and the left operand does not
113569 ** and the left operand might be NULL, then do an IsNull check
113570 ** check on the left operand before computing the right operand.
 
113571 */
113572 if( ExprHasProperty(pExpr->pRight, EP_Subquery)
113573 && sqlite3ExprCanBeNull(pExpr->pLeft)
113574 ){
113575 addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r1);
@@ -114645,11 +115129,10 @@
115129 int destIfNull /* Jump here if the results are unknown due to NULLs */
115130 ){
115131 int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
115132 int eType; /* Type of the RHS */
115133 int rLhs; /* Register(s) holding the LHS values */
 
115134 Vdbe *v; /* Statement under construction */
115135 int *aiMap = 0; /* Map from vector field to index column */
115136 char *zAff = 0; /* Affinity string for comparisons */
115137 int nVector; /* Size of vectors for this IN operator */
115138 int iDummy; /* Dummy parameter to exprCodeVector() */
@@ -114708,23 +115191,12 @@
115191 ** Avoid factoring the LHS of the IN(...) expression out of the loop,
115192 ** even if it is constant, as OP_Affinity may be used on the register
115193 ** by code generated below. */
115194 assert( pParse->okConstFactor==okConstFactor );
115195 pParse->okConstFactor = 0;
115196 rLhs = exprCodeVector(pParse, pLeft, &iDummy);
115197 pParse->okConstFactor = okConstFactor;
 
 
 
 
 
 
 
 
 
 
 
115198
115199 /* If sqlite3FindInIndex() did not find or create an index that is
115200 ** suitable for evaluating the IN operator, then evaluate using a
115201 ** sequence of comparisons.
115202 **
@@ -114735,10 +115207,11 @@
115207 CollSeq *pColl;
115208 int labelOk = sqlite3VdbeMakeLabel(pParse);
115209 int r2, regToFree;
115210 int regCkNull = 0;
115211 int ii;
115212 assert( nVector==1 );
115213 assert( ExprUseXList(pExpr) );
115214 pList = pExpr->x.pList;
115215 pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
115216 if( destIfNull!=destIfFalse ){
115217 regCkNull = sqlite3GetTempReg(pParse);
@@ -114775,10 +115248,30 @@
115248 }
115249 sqlite3VdbeResolveLabel(v, labelOk);
115250 sqlite3ReleaseTempReg(pParse, regCkNull);
115251 goto sqlite3ExprCodeIN_finished;
115252 }
115253
115254 if( eType!=IN_INDEX_ROWID ){
115255 /* If this IN operator will use an index, then the order of columns in the
115256 ** vector might be different from the order in the index. In that case,
115257 ** we need to reorder the LHS values to be in index order. Run Affinity
115258 ** before reordering the columns, so that the affinity is correct.
115259 */
115260 sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
115261 for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
115262 if( i!=nVector ){
115263 /* Need to reorder the LHS fields according to aiMap */
115264 int rLhsOrig = rLhs;
115265 rLhs = sqlite3GetTempRange(pParse, nVector);
115266 for(i=0; i<nVector; i++){
115267 sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
115268 }
115269 sqlite3ReleaseTempReg(pParse, rLhsOrig);
115270 }
115271 }
115272
115273
115274 /* Step 2: Check to see if the LHS contains any NULL columns. If the
115275 ** LHS does contain NULLs then the result must be either FALSE or NULL.
115276 ** We will then skip the binary search of the RHS.
115277 */
@@ -114802,15 +115295,15 @@
115295 */
115296 if( eType==IN_INDEX_ROWID ){
115297 /* In this case, the RHS is the ROWID of table b-tree and so we also
115298 ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
115299 ** into a single opcode. */
115300 assert( nVector==1 );
115301 sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs);
115302 VdbeCoverage(v);
115303 addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
115304 }else{
 
115305 if( destIfFalse==destIfNull ){
115306 /* Combine Step 3 and Step 5 into a single opcode */
115307 if( ExprHasProperty(pExpr, EP_Subrtn) ){
115308 const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
115309 assert( pOp->opcode==OP_Once || pParse->nErr );
@@ -114884,11 +115377,10 @@
115377
115378 /* Jumps here in order to return true. */
115379 sqlite3VdbeJumpHere(v, addrTruthOp);
115380
115381 sqlite3ExprCodeIN_finished:
 
115382 VdbeComment((v, "end IN expr"));
115383 sqlite3ExprCodeIN_oom_error:
115384 sqlite3DbFree(pParse->db, aiMap);
115385 sqlite3DbFree(pParse->db, zAff);
115386 }
@@ -115442,10 +115934,84 @@
115934 }
115935 }
115936 return 0;
115937 }
115938
115939 /*
115940 ** Generate code that evaluates an AND or OR operator leaving a
115941 ** boolean result in a register. pExpr is the AND/OR expression.
115942 ** Store the result in the "target" register. Use short-circuit
115943 ** evaluation to avoid computing both operands, if possible.
115944 **
115945 ** The code generated might require the use of a temporary register.
115946 ** If it does, then write the number of that temporary register
115947 ** into *pTmpReg. If not, leave *pTmpReg unchanged.
115948 */
115949 static SQLITE_NOINLINE int exprCodeTargetAndOr(
115950 Parse *pParse, /* Parsing context */
115951 Expr *pExpr, /* AND or OR expression to be coded */
115952 int target, /* Put result in this register, guaranteed */
115953 int *pTmpReg /* Write a temporary register here */
115954 ){
115955 int op; /* The opcode. TK_AND or TK_OR */
115956 int skipOp; /* Opcode for the branch that skips one operand */
115957 int addrSkip; /* Branch instruction that skips one of the operands */
115958 int regSS = 0; /* Register holding computed operand when other omitted */
115959 int r1, r2; /* Registers for left and right operands, respectively */
115960 Expr *pAlt; /* Alternative, simplified expression */
115961 Vdbe *v; /* statement being coded */
115962
115963 assert( pExpr!=0 );
115964 op = pExpr->op;
115965 assert( op==TK_AND || op==TK_OR );
115966 assert( TK_AND==OP_And ); testcase( op==TK_AND );
115967 assert( TK_OR==OP_Or ); testcase( op==TK_OR );
115968 assert( pParse->pVdbe!=0 );
115969 v = pParse->pVdbe;
115970 pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
115971 if( pAlt!=pExpr ){
115972 r1 = sqlite3ExprCodeTarget(pParse, pAlt, target);
115973 sqlite3VdbeAddOp3(v, OP_BitAnd, r1, r1, target);
115974 return target;
115975 }
115976 skipOp = op==TK_AND ? OP_IfNot : OP_If;
115977 if( exprEvalRhsFirst(pExpr) ){
115978 /* Compute the right operand first. Skip the computation of the left
115979 ** operand if the right operand fully determines the result */
115980 r2 = regSS = sqlite3ExprCodeTarget(pParse, pExpr->pRight, target);
115981 addrSkip = sqlite3VdbeAddOp1(v, skipOp, r2);
115982 VdbeComment((v, "skip left operand"));
115983 VdbeCoverage(v);
115984 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pTmpReg);
115985 }else{
115986 /* Compute the left operand first */
115987 r1 = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
115988 if( ExprHasProperty(pExpr->pRight, EP_Subquery) ){
115989 /* Skip over the computation of the right operand if the right
115990 ** operand is a subquery and the left operand completely determines
115991 ** the result */
115992 regSS = r1;
115993 addrSkip = sqlite3VdbeAddOp1(v, skipOp, r1);
115994 VdbeComment((v, "skip right operand"));
115995 VdbeCoverage(v);
115996 }else{
115997 addrSkip = regSS = 0;
115998 }
115999 r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pTmpReg);
116000 }
116001 sqlite3VdbeAddOp3(v, op, r2, r1, target);
116002 testcase( (*pTmpReg)==0 );
116003 if( addrSkip ){
116004 sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
116005 sqlite3VdbeJumpHere(v, addrSkip);
116006 sqlite3VdbeAddOp3(v, OP_Or, regSS, regSS, target);
116007 VdbeComment((v, "short-circut value"));
116008 }
116009 return target;
116010 }
116011
116012
116013
116014 /*
116015 ** Generate code into the current Vdbe to evaluate the given
116016 ** expression. Attempt to store the results in register "target".
116017 ** Return the register where results are stored.
@@ -115633,10 +116199,16 @@
116199 case TK_STRING: {
116200 assert( !ExprHasProperty(pExpr, EP_IntValue) );
116201 sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
116202 return target;
116203 }
116204 case TK_NULLS: {
116205 /* Set a range of registers to NULL. pExpr->y.nReg registers starting
116206 ** with target */
116207 sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
116208 return target;
116209 }
116210 default: {
116211 /* Make NULL the default case so that if a bug causes an illegal
116212 ** Expr node to be passed into this function, it will be handled
116213 ** sanely and not crash. But keep the assert() to bring the problem
116214 ** to the attention of the developers. */
@@ -115724,16 +116296,18 @@
116296 sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
116297 }
116298 }
116299 testcase( regFree1==0 );
116300 testcase( regFree2==0 );
 
116301 }
116302 break;
116303 }
116304 case TK_AND:
116305 case TK_OR: {
116306 inReg = exprCodeTargetAndOr(pParse, pExpr, target, &regFree1);
116307 break;
116308 }
116309 case TK_PLUS:
116310 case TK_STAR:
116311 case TK_MINUS:
116312 case TK_REM:
116313 case TK_BITAND:
@@ -115741,12 +116315,10 @@
116315 case TK_SLASH:
116316 case TK_LSHIFT:
116317 case TK_RSHIFT:
116318 case TK_CONCAT: {
116319 int addrIsNull;
 
 
116320 assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
116321 assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS );
116322 assert( TK_REM==OP_Remainder ); testcase( op==TK_REM );
116323 assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND );
116324 assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR );
@@ -116341,10 +116913,29 @@
116913 }
116914 pParse->pConstExpr = p;
116915 }
116916 return regDest;
116917 }
116918
116919 /*
116920 ** Make arrangements to invoke OP_Null on a range of registers
116921 ** during initialization.
116922 */
116923 SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
116924 Parse *pParse, /* Parsing context */
116925 int iReg, /* First register to set to NULL */
116926 int nReg /* Number of sequential registers to NULL out */
116927 ){
116928 u8 okConstFactor = pParse->okConstFactor;
116929 Expr t;
116930 memset(&t, 0, sizeof(t));
116931 t.op = TK_NULLS;
116932 t.y.nReg = nReg;
116933 pParse->okConstFactor = 1;
116934 sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
116935 pParse->okConstFactor = okConstFactor;
116936 }
116937
116938 /*
116939 ** Generate code to evaluate an expression and store the results
116940 ** into a register. Return the register number where the results
116941 ** are stored.
@@ -123872,10 +124463,20 @@
124463 if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
124464 Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
124465 if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
124466 pMod = sqlite3PragmaVtabRegister(db, zName);
124467 }
124468 #ifndef SQLITE_OMIT_JSON
124469 if( pMod==0 && sqlite3_strnicmp(zName, "json", 4)==0 ){
124470 pMod = sqlite3JsonVtabRegister(db, zName);
124471 }
124472 #endif
124473 #ifdef SQLITE_ENABLE_CARRAY
124474 if( pMod==0 && sqlite3_stricmp(zName, "carray")==0 ){
124475 pMod = sqlite3CarrayRegister(db);
124476 }
124477 #endif
124478 if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
124479 testcase( pMod->pEpoTab==0 );
124480 return pMod->pEpoTab;
124481 }
124482 }
@@ -124510,11 +125111,11 @@
125111 */
125112 SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
125113 int i;
125114 i16 iCol16;
125115 assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
125116 assert( pIdx->nColumn<=SQLITE_MAX_COLUMN*2 );
125117 iCol16 = iCol;
125118 for(i=0; i<pIdx->nColumn; i++){
125119 if( iCol16==pIdx->aiColumn[i] ){
125120 return i;
125121 }
@@ -124807,10 +125408,13 @@
125408 sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
125409 sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
125410 sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
125411 sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
125412 sqlite3VdbeAddOp0(v, OP_Close);
125413 }else if( db->init.imposterTable ){
125414 pTable->tabFlags |= TF_Imposter;
125415 if( db->init.imposterTable>=2 ) pTable->tabFlags |= TF_Readonly;
125416 }
125417
125418 /* Normal (non-error) return. */
125419 return;
125420
@@ -129102,18 +129706,23 @@
129706 pKey->aSortFlags[i] = pIdx->aSortOrder[i];
129707 assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
129708 }
129709 if( pParse->nErr ){
129710 assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
129711 if( pIdx->bNoQuery==0
129712 && sqlite3HashFind(&pIdx->pSchema->idxHash, pIdx->zName)
129713 ){
129714 /* Deactivate the index because it contains an unknown collating
129715 ** sequence. The only way to reactive the index is to reload the
129716 ** schema. Adding the missing collating sequence later does not
129717 ** reactive the index. The application had the chance to register
129718 ** the missing index using the collation-needed callback. For
129719 ** simplicity, SQLite will not give the application a second chance.
129720 **
129721 ** Except, do not do this if the index is not in the schema hash
129722 ** table. In this case the index is currently being constructed
129723 ** by a CREATE INDEX statement, and retrying will not help. */
129724 pIdx->bNoQuery = 1;
129725 pParse->rc = SQLITE_ERROR_RETRY;
129726 }
129727 sqlite3KeyInfoUnref(pKey);
129728 pKey = 0;
@@ -131977,11 +132586,11 @@
132586 ** a decoding of those digits into *pVal. Or return false if any
132587 ** one of the first N characters in z[] is not a hexadecimal digit.
132588 */
132589 static int isNHex(const char *z, int N, u32 *pVal){
132590 int i;
132591 u32 v = 0;
132592 for(i=0; i<N; i++){
132593 if( !sqlite3Isxdigit(z[i]) ) return 0;
132594 v = (v<<4) + sqlite3HexToInt(z[i]);
132595 }
132596 *pVal = v;
@@ -132490,10 +133099,11 @@
133099 int nSep,
133100 const char *zSep
133101 ){
133102 i64 j, n = 0;
133103 int i;
133104 int bNotNull = 0; /* True after at least NOT NULL argument seen */
133105 char *z;
133106 for(i=0; i<argc; i++){
133107 n += sqlite3_value_bytes(argv[i]);
133108 }
133109 n += (argc-1)*(i64)nSep;
@@ -132506,16 +133116,17 @@
133116 for(i=0; i<argc; i++){
133117 if( sqlite3_value_type(argv[i])!=SQLITE_NULL ){
133118 int k = sqlite3_value_bytes(argv[i]);
133119 const char *v = (const char*)sqlite3_value_text(argv[i]);
133120 if( v!=0 ){
133121 if( bNotNull && nSep>0 ){
133122 memcpy(&z[j], zSep, nSep);
133123 j += nSep;
133124 }
133125 memcpy(&z[j], v, k);
133126 j += k;
133127 bNotNull = 1;
133128 }
133129 }
133130 }
133131 z[j] = 0;
133132 assert( j<=n );
@@ -133456,10 +134067,456 @@
134067 type0 = sqlite3_value_numeric_type(argv[0]);
134068 if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
134069 x = sqlite3_value_double(argv[0]);
134070 sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
134071 }
134072
134073 #if defined(SQLITE_ENABLE_PERCENTILE)
134074 /***********************************************************************
134075 ** This section implements the percentile(Y,P) SQL function and similar.
134076 ** Requirements:
134077 **
134078 ** (1) The percentile(Y,P) function is an aggregate function taking
134079 ** exactly two arguments.
134080 **
134081 ** (2) If the P argument to percentile(Y,P) is not the same for every
134082 ** row in the aggregate then an error is thrown. The word "same"
134083 ** in the previous sentence means that the value differ by less
134084 ** than 0.001.
134085 **
134086 ** (3) If the P argument to percentile(Y,P) evaluates to anything other
134087 ** than a number in the range of 0.0 to 100.0 inclusive then an
134088 ** error is thrown.
134089 **
134090 ** (4) If any Y argument to percentile(Y,P) evaluates to a value that
134091 ** is not NULL and is not numeric then an error is thrown.
134092 **
134093 ** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
134094 ** infinity then an error is thrown. (SQLite always interprets NaN
134095 ** values as NULL.)
134096 **
134097 ** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
134098 ** including CASE WHEN expressions.
134099 **
134100 ** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
134101 ** one million (1,000,000) rows.
134102 **
134103 ** (8) If there are no non-NULL values for Y, then percentile(Y,P)
134104 ** returns NULL.
134105 **
134106 ** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
134107 ** returns the one Y value.
134108 **
134109 ** (10) If there N non-NULL values of Y where N is two or more and
134110 ** the Y values are ordered from least to greatest and a graph is
134111 ** drawn from 0 to N-1 such that the height of the graph at J is
134112 ** the J-th Y value and such that straight lines are drawn between
134113 ** adjacent Y values, then the percentile(Y,P) function returns
134114 ** the height of the graph at P*(N-1)/100.
134115 **
134116 ** (11) The percentile(Y,P) function always returns either a floating
134117 ** point number or NULL.
134118 **
134119 ** (12) The percentile(Y,P) is implemented as a single C99 source-code
134120 ** file that compiles into a shared-library or DLL that can be loaded
134121 ** into SQLite using the sqlite3_load_extension() interface.
134122 **
134123 ** (13) A separate median(Y) function is the equivalent percentile(Y,50).
134124 **
134125 ** (14) A separate percentile_cont(Y,P) function is equivalent to
134126 ** percentile(Y,P/100.0). In other words, the fraction value in
134127 ** the second argument is in the range of 0 to 1 instead of 0 to 100.
134128 **
134129 ** (15) A separate percentile_disc(Y,P) function is like
134130 ** percentile_cont(Y,P) except that instead of returning the weighted
134131 ** average of the nearest two input values, it returns the next lower
134132 ** value. So the percentile_disc(Y,P) will always return a value
134133 ** that was one of the inputs.
134134 **
134135 ** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
134136 ** percentile_disc(Y,P) can be used as window functions.
134137 **
134138 ** Differences from standard SQL:
134139 **
134140 ** * The percentile_cont(X,P) function is equivalent to the following in
134141 ** standard SQL:
134142 **
134143 ** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
134144 **
134145 ** The SQLite syntax is much more compact. The standard SQL syntax
134146 ** is also supported if SQLite is compiled with the
134147 ** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
134148 **
134149 ** * No median(X) function exists in the SQL standard. App developers
134150 ** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
134151 **
134152 ** * No percentile(Y,P) function exists in the SQL standard. Instead of
134153 ** percential(Y,P), developers must write this:
134154 ** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
134155 ** the fraction parameter to percentile() goes from 0 to 100 whereas
134156 ** the fraction parameter in SQL standard percentile_cont() goes from
134157 ** 0 to 1.
134158 **
134159 ** Implementation notes as of 2024-08-31:
134160 **
134161 ** * The regular aggregate-function versions of these routines work
134162 ** by accumulating all values in an array of doubles, then sorting
134163 ** that array using quicksort before computing the answer. Thus
134164 ** the runtime is O(NlogN) where N is the number of rows of input.
134165 **
134166 ** * For the window-function versions of these routines, the array of
134167 ** inputs is sorted as soon as the first value is computed. Thereafter,
134168 ** the array is kept in sorted order using an insert-sort. This
134169 ** results in O(N*K) performance where K is the size of the window.
134170 ** One can imagine alternative implementations that give O(N*logN*logK)
134171 ** performance, but they require more complex logic and data structures.
134172 ** The developers have elected to keep the asymptotically slower
134173 ** algorithm for now, for simplicity, under the theory that window
134174 ** functions are seldom used and when they are, the window size K is
134175 ** often small. The developers might revisit that decision later,
134176 ** should the need arise.
134177 */
134178
134179 /* The following object is the group context for a single percentile()
134180 ** aggregate. Remember all input Y values until the very end.
134181 ** Those values are accumulated in the Percentile.a[] array.
134182 */
134183 typedef struct Percentile Percentile;
134184 struct Percentile {
134185 unsigned nAlloc; /* Number of slots allocated for a[] */
134186 unsigned nUsed; /* Number of slots actually used in a[] */
134187 char bSorted; /* True if a[] is already in sorted order */
134188 char bKeepSorted; /* True if advantageous to keep a[] sorted */
134189 char bPctValid; /* True if rPct is valid */
134190 double rPct; /* Fraction. 0.0 to 1.0 */
134191 double *a; /* Array of Y values */
134192 };
134193
134194 /*
134195 ** Return TRUE if the input floating-point number is an infinity.
134196 */
134197 static int percentIsInfinity(double r){
134198 sqlite3_uint64 u;
134199 assert( sizeof(u)==sizeof(r) );
134200 memcpy(&u, &r, sizeof(u));
134201 return ((u>>52)&0x7ff)==0x7ff;
134202 }
134203
134204 /*
134205 ** Return TRUE if two doubles differ by 0.001 or less.
134206 */
134207 static int percentSameValue(double a, double b){
134208 a -= b;
134209 return a>=-0.001 && a<=0.001;
134210 }
134211
134212 /*
134213 ** Search p (which must have p->bSorted) looking for an entry with
134214 ** value y. Return the index of that entry.
134215 **
134216 ** If bExact is true, return -1 if the entry is not found.
134217 **
134218 ** If bExact is false, return the index at which a new entry with
134219 ** value y should be insert in order to keep the values in sorted
134220 ** order. The smallest return value in this case will be 0, and
134221 ** the largest return value will be p->nUsed.
134222 */
134223 static int percentBinarySearch(Percentile *p, double y, int bExact){
134224 int iFirst = 0; /* First element of search range */
134225 int iLast = p->nUsed - 1; /* Last element of search range */
134226 while( iLast>=iFirst ){
134227 int iMid = (iFirst+iLast)/2;
134228 double x = p->a[iMid];
134229 if( x<y ){
134230 iFirst = iMid + 1;
134231 }else if( x>y ){
134232 iLast = iMid - 1;
134233 }else{
134234 return iMid;
134235 }
134236 }
134237 if( bExact ) return -1;
134238 return iFirst;
134239 }
134240
134241 /*
134242 ** Generate an error for a percentile function.
134243 **
134244 ** The error format string must have exactly one occurrence of "%%s()"
134245 ** (with two '%' characters). That substring will be replaced by the name
134246 ** of the function.
134247 */
134248 static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
134249 char *zMsg1;
134250 char *zMsg2;
134251 va_list ap;
134252
134253 va_start(ap, zFormat);
134254 zMsg1 = sqlite3_vmprintf(zFormat, ap);
134255 va_end(ap);
134256 zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, sqlite3VdbeFuncName(pCtx)) : 0;
134257 sqlite3_result_error(pCtx, zMsg2, -1);
134258 sqlite3_free(zMsg1);
134259 sqlite3_free(zMsg2);
134260 }
134261
134262 /*
134263 ** The "step" function for percentile(Y,P) is called once for each
134264 ** input row.
134265 */
134266 static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
134267 Percentile *p;
134268 double rPct;
134269 int eType;
134270 double y;
134271 assert( argc==2 || argc==1 );
134272
134273 if( argc==1 ){
134274 /* Requirement 13: median(Y) is the same as percentile(Y,50). */
134275 rPct = 0.5;
134276 }else{
134277 /* P must be a number between 0 and 100 for percentile() or between
134278 ** 0.0 and 1.0 for percentile_cont() and percentile_disc().
134279 **
134280 ** The user-data is an integer which is 10 times the upper bound.
134281 */
134282 double mxFrac = (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx))&2)? 100.0 : 1.0;
134283 eType = sqlite3_value_numeric_type(argv[1]);
134284 rPct = sqlite3_value_double(argv[1])/mxFrac;
134285 if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
134286 || rPct<0.0 || rPct>1.0
134287 ){
134288 percentError(pCtx, "the fraction argument to %%s()"
134289 " is not between 0.0 and %.1f",
134290 (double)mxFrac);
134291 return;
134292 }
134293 }
134294
134295 /* Allocate the session context. */
134296 p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
134297 if( p==0 ) return;
134298
134299 /* Remember the P value. Throw an error if the P value is different
134300 ** from any prior row, per Requirement (2). */
134301 if( !p->bPctValid ){
134302 p->rPct = rPct;
134303 p->bPctValid = 1;
134304 }else if( !percentSameValue(p->rPct,rPct) ){
134305 percentError(pCtx, "the fraction argument to %%s()"
134306 " is not the same for all input rows");
134307 return;
134308 }
134309
134310 /* Ignore rows for which Y is NULL */
134311 eType = sqlite3_value_type(argv[0]);
134312 if( eType==SQLITE_NULL ) return;
134313
134314 /* If not NULL, then Y must be numeric. Otherwise throw an error.
134315 ** Requirement 4 */
134316 if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
134317 percentError(pCtx, "input to %%s() is not numeric");
134318 return;
134319 }
134320
134321 /* Throw an error if the Y value is infinity or NaN */
134322 y = sqlite3_value_double(argv[0]);
134323 if( percentIsInfinity(y) ){
134324 percentError(pCtx, "Inf input to %%s()");
134325 return;
134326 }
134327
134328 /* Allocate and store the Y */
134329 if( p->nUsed>=p->nAlloc ){
134330 unsigned n = p->nAlloc*2 + 250;
134331 double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
134332 if( a==0 ){
134333 sqlite3_free(p->a);
134334 memset(p, 0, sizeof(*p));
134335 sqlite3_result_error_nomem(pCtx);
134336 return;
134337 }
134338 p->nAlloc = n;
134339 p->a = a;
134340 }
134341 if( p->nUsed==0 ){
134342 p->a[p->nUsed++] = y;
134343 p->bSorted = 1;
134344 }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
134345 p->a[p->nUsed++] = y;
134346 }else if( p->bKeepSorted ){
134347 int i;
134348 i = percentBinarySearch(p, y, 0);
134349 if( i<(int)p->nUsed ){
134350 memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
134351 }
134352 p->a[i] = y;
134353 p->nUsed++;
134354 }else{
134355 p->a[p->nUsed++] = y;
134356 p->bSorted = 0;
134357 }
134358 }
134359
134360 /*
134361 ** Interchange two doubles.
134362 */
134363 #define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
134364
134365 /*
134366 ** Sort an array of doubles.
134367 **
134368 ** Algorithm: quicksort
134369 **
134370 ** This is implemented separately rather than using the qsort() routine
134371 ** from the standard library because:
134372 **
134373 ** (1) To avoid a dependency on qsort()
134374 ** (2) To avoid the function call to the comparison routine for each
134375 ** comparison.
134376 */
134377 static void percentSort(double *a, unsigned int n){
134378 int iLt; /* Entries before a[iLt] are less than rPivot */
134379 int iGt; /* Entries at or after a[iGt] are greater than rPivot */
134380 int i; /* Loop counter */
134381 double rPivot; /* The pivot value */
134382
134383 assert( n>=2 );
134384 if( a[0]>a[n-1] ){
134385 SWAP_DOUBLE(a[0],a[n-1])
134386 }
134387 if( n==2 ) return;
134388 iGt = n-1;
134389 i = n/2;
134390 if( a[0]>a[i] ){
134391 SWAP_DOUBLE(a[0],a[i])
134392 }else if( a[i]>a[iGt] ){
134393 SWAP_DOUBLE(a[i],a[iGt])
134394 }
134395 if( n==3 ) return;
134396 rPivot = a[i];
134397 iLt = i = 1;
134398 do{
134399 if( a[i]<rPivot ){
134400 if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
134401 iLt++;
134402 i++;
134403 }else if( a[i]>rPivot ){
134404 do{
134405 iGt--;
134406 }while( iGt>i && a[iGt]>rPivot );
134407 SWAP_DOUBLE(a[i],a[iGt])
134408 }else{
134409 i++;
134410 }
134411 }while( i<iGt );
134412 if( iLt>=2 ) percentSort(a, iLt);
134413 if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
134414
134415 /* Uncomment for testing */
134416 #if 0
134417 for(i=0; i<n-1; i++){
134418 assert( a[i]<=a[i+1] );
134419 }
134420 #endif
134421 }
134422
134423
134424 /*
134425 ** The "inverse" function for percentile(Y,P) is called to remove a
134426 ** row that was previously inserted by "step".
134427 */
134428 static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
134429 Percentile *p;
134430 int eType;
134431 double y;
134432 int i;
134433 assert( argc==2 || argc==1 );
134434
134435 /* Allocate the session context. */
134436 p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
134437 assert( p!=0 );
134438
134439 /* Ignore rows for which Y is NULL */
134440 eType = sqlite3_value_type(argv[0]);
134441 if( eType==SQLITE_NULL ) return;
134442
134443 /* If not NULL, then Y must be numeric. Otherwise throw an error.
134444 ** Requirement 4 */
134445 if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
134446 return;
134447 }
134448
134449 /* Ignore the Y value if it is infinity or NaN */
134450 y = sqlite3_value_double(argv[0]);
134451 if( percentIsInfinity(y) ){
134452 return;
134453 }
134454 if( p->bSorted==0 ){
134455 assert( p->nUsed>1 );
134456 percentSort(p->a, p->nUsed);
134457 p->bSorted = 1;
134458 }
134459 p->bKeepSorted = 1;
134460
134461 /* Find and remove the row */
134462 i = percentBinarySearch(p, y, 1);
134463 if( i>=0 ){
134464 p->nUsed--;
134465 if( i<(int)p->nUsed ){
134466 memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
134467 }
134468 }
134469 }
134470
134471 /*
134472 ** Compute the final output of percentile(). Clean up all allocated
134473 ** memory if and only if bIsFinal is true.
134474 */
134475 static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
134476 Percentile *p;
134477 int settings = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx))&1; /* Discrete? */
134478 unsigned i1, i2;
134479 double v1, v2;
134480 double ix, vx;
134481 p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
134482 if( p==0 ) return;
134483 if( p->a==0 ) return;
134484 if( p->nUsed ){
134485 if( p->bSorted==0 ){
134486 assert( p->nUsed>1 );
134487 percentSort(p->a, p->nUsed);
134488 p->bSorted = 1;
134489 }
134490 ix = p->rPct*(p->nUsed-1);
134491 i1 = (unsigned)ix;
134492 if( settings & 1 ){
134493 vx = p->a[i1];
134494 }else{
134495 i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
134496 v1 = p->a[i1];
134497 v2 = p->a[i2];
134498 vx = v1 + (v2-v1)*(ix-i1);
134499 }
134500 sqlite3_result_double(pCtx, vx);
134501 }
134502 if( bIsFinal ){
134503 sqlite3_free(p->a);
134504 memset(p, 0, sizeof(*p));
134505 }else{
134506 p->bKeepSorted = 1;
134507 }
134508 }
134509 static void percentFinal(sqlite3_context *pCtx){
134510 percentCompute(pCtx, 1);
134511 }
134512 static void percentValue(sqlite3_context *pCtx){
134513 percentCompute(pCtx, 0);
134514 }
134515 /****** End of percentile family of functions ******/
134516 #endif /* SQLITE_ENABLE_PERCENTILE */
134517
134518
134519 #ifdef SQLITE_DEBUG
134520 /*
134521 ** Implementation of fpdecode(x,y,z) function.
134522 **
@@ -133687,10 +134744,25 @@
134744 WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
134745 groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
134746 WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
134747 groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
134748
134749 #ifdef SQLITE_ENABLE_PERCENTILE
134750 WAGGREGATE(median, 1, 0,0, percentStep,
134751 percentFinal, percentValue, percentInverse,
134752 SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134753 WAGGREGATE(percentile, 2, 0x2,0, percentStep,
134754 percentFinal, percentValue, percentInverse,
134755 SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134756 WAGGREGATE(percentile_cont, 2, 0,0, percentStep,
134757 percentFinal, percentValue, percentInverse,
134758 SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134759 WAGGREGATE(percentile_disc, 2, 0x1,0, percentStep,
134760 percentFinal, percentValue, percentInverse,
134761 SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
134762 #endif /* SQLITE_ENABLE_PERCENTILE */
134763
134764 LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
134765 #ifdef SQLITE_CASE_SENSITIVE_LIKE
134766 LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
134767 LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
134768 #else
@@ -139184,10 +140256,14 @@
140256 /* Version 3.44.0 and later */
140257 void *(*get_clientdata)(sqlite3*,const char*);
140258 int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
140259 /* Version 3.50.0 and later */
140260 int (*setlk_timeout)(sqlite3*,int,int);
140261 /* Version 3.51.0 and later */
140262 int (*set_errmsg)(sqlite3*,int,const char*);
140263 int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
140264
140265 };
140266
140267 /*
140268 ** This is the function signature used for all extension entry points. It
140269 ** is also defined in the file "loadext.c".
@@ -139519,10 +140595,13 @@
140595 /* Version 3.44.0 and later */
140596 #define sqlite3_get_clientdata sqlite3_api->get_clientdata
140597 #define sqlite3_set_clientdata sqlite3_api->set_clientdata
140598 /* Version 3.50.0 and later */
140599 #define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
140600 /* Version 3.51.0 and later */
140601 #define sqlite3_set_errmsg sqlite3_api->set_errmsg
140602 #define sqlite3_db_status64 sqlite3_api->db_status64
140603 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
140604
140605 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
140606 /* This case when the file really is being compiled as a loadable
140607 ** extension */
@@ -140042,11 +141121,14 @@
141121 sqlite3_stmt_explain,
141122 /* Version 3.44.0 and later */
141123 sqlite3_get_clientdata,
141124 sqlite3_set_clientdata,
141125 /* Version 3.50.0 and later */
141126 sqlite3_setlk_timeout,
141127 /* Version 3.51.0 and later */
141128 sqlite3_set_errmsg,
141129 sqlite3_db_status64
141130 };
141131
141132 /* True if x is the directory separator character
141133 */
141134 #if SQLITE_OS_WIN
@@ -141503,10 +142585,26 @@
142585 addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1);
142586 VdbeCoverage(v);
142587 sqlite3VdbeAddOp0(v, OP_Halt);
142588 return addr;
142589 }
142590
142591 /*
142592 ** Should table pTab be skipped when doing an integrity_check?
142593 ** Return true or false.
142594 **
142595 ** If pObjTab is not null, the return true if pTab matches pObjTab.
142596 **
142597 ** If pObjTab is null, then return true only if pTab is an imposter table.
142598 */
142599 static int tableSkipIntegrityCheck(const Table *pTab, const Table *pObjTab){
142600 if( pObjTab ){
142601 return pTab!=pObjTab;
142602 }else{
142603 return (pTab->tabFlags & TF_Imposter)!=0;
142604 }
142605 }
142606
142607 /*
142608 ** Process a pragma statement.
142609 **
142610 ** Pragmas are of this form:
@@ -142849,11 +143947,11 @@
143947 pTbls = &db->aDb[i].pSchema->tblHash;
143948 for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
143949 Table *pTab = sqliteHashData(x); /* Current table */
143950 Index *pIdx; /* An index on pTab */
143951 int nIdx; /* Number of indexes on pTab */
143952 if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
143953 if( HasRowid(pTab) ) cnt++;
143954 for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
143955 }
143956 if( cnt==0 ) continue;
143957 if( pObjTab ) cnt++;
@@ -142862,11 +143960,11 @@
143960 cnt = 0;
143961 if( pObjTab ) aRoot[++cnt] = 0;
143962 for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
143963 Table *pTab = sqliteHashData(x);
143964 Index *pIdx;
143965 if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
143966 if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
143967 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
143968 aRoot[++cnt] = pIdx->tnum;
143969 }
143970 }
@@ -142893,11 +143991,11 @@
143991 sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
143992 for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
143993 int iTab = 0;
143994 Table *pTab = sqliteHashData(x);
143995 Index *pIdx;
143996 if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
143997 if( HasRowid(pTab) ){
143998 iTab = cnt++;
143999 }else{
144000 iTab = cnt;
144001 for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){
@@ -142929,11 +144027,11 @@
144027 int r1 = -1;
144028 int bStrict; /* True for a STRICT table */
144029 int r2; /* Previous key for WITHOUT ROWID tables */
144030 int mxCol; /* Maximum non-virtual column number */
144031
144032 if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
144033 if( !IsOrdinaryTable(pTab) ) continue;
144034 if( isQuick || HasRowid(pTab) ){
144035 pPk = 0;
144036 r2 = 0;
144037 }else{
@@ -143253,11 +144351,11 @@
144351 */
144352 for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
144353 Table *pTab = sqliteHashData(x);
144354 sqlite3_vtab *pVTab;
144355 int a1;
144356 if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
144357 if( IsOrdinaryTable(pTab) ) continue;
144358 if( !IsVirtual(pTab) ) continue;
144359 if( pTab->nCol<=0 ){
144360 const char *zMod = pTab->u.vtab.azArg[0];
144361 if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
@@ -143485,10 +144583,12 @@
144583 eMode = SQLITE_CHECKPOINT_FULL;
144584 }else if( sqlite3StrICmp(zRight, "restart")==0 ){
144585 eMode = SQLITE_CHECKPOINT_RESTART;
144586 }else if( sqlite3StrICmp(zRight, "truncate")==0 ){
144587 eMode = SQLITE_CHECKPOINT_TRUNCATE;
144588 }else if( sqlite3StrICmp(zRight, "noop")==0 ){
144589 eMode = SQLITE_CHECKPOINT_NOOP;
144590 }
144591 }
144592 pParse->nMem = 3;
144593 sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
144594 sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
@@ -145051,13 +146151,15 @@
146151 ** or encounters a permanent error. A schema problem after one schema
146152 ** reset is considered a permanent error. */
146153 rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
146154 assert( rc==SQLITE_OK || *ppStmt==0 );
146155 if( rc==SQLITE_OK || db->mallocFailed ) break;
146156 cnt++;
146157 }while( (rc==SQLITE_ERROR_RETRY && ALWAYS(cnt<=SQLITE_MAX_PREPARE_RETRY))
146158 || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt)==1) );
146159 sqlite3BtreeLeaveAll(db);
146160 assert( rc!=SQLITE_ERROR_RETRY );
146161 rc = sqlite3ApiExit(db, rc);
146162 assert( (rc&db->errMask)==rc );
146163 db->busyHandler.nBusy = 0;
146164 sqlite3_mutex_leave(db->mutex);
146165 assert( rc==SQLITE_OK || (*ppStmt)==0 );
@@ -145727,12 +146829,11 @@
146829 while( p ){
146830 ExprSetProperty(p, joinFlag);
146831 assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
146832 ExprSetVVAProperty(p, EP_NoReduce);
146833 p->w.iJoin = iTable;
146834 if( ExprUseXList(p) ){
 
146835 if( p->x.pList ){
146836 int i;
146837 for(i=0; i<p->x.pList->nExpr; i++){
146838 sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
146839 }
@@ -145944,10 +147045,11 @@
147045 else if( pRight->u3.pOn ){
147046 sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
147047 p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
147048 pRight->u3.pOn = 0;
147049 pRight->fg.isOn = 1;
147050 p->selFlags |= SF_OnToWhere;
147051 }
147052 }
147053 return 0;
147054 }
147055
@@ -146830,11 +147932,14 @@
147932 ** Allocate a KeyInfo object sufficient for an index of N key columns and
147933 ** X extra columns.
147934 */
147935 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
147936 int nExtra = (N+X)*(sizeof(CollSeq*)+1);
147937 KeyInfo *p;
147938 assert( X>=0 );
147939 if( NEVER(N+X>0xffff) ) return (KeyInfo*)sqlite3OomFault(db);
147940 p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
147941 if( p ){
147942 p->aSortFlags = (u8*)&p->aColl[N+X];
147943 p->nKeyField = (u16)N;
147944 p->nAllField = (u16)(N+X);
147945 p->enc = ENC(db);
@@ -149149,11 +150254,11 @@
150254 ** expressions in pEList.
150255 **
150256 ** ## About "isOuterJoin":
150257 **
150258 ** The isOuterJoin column indicates that the replacement will occur into a
150259 ** position in the parent that is NULL-able due to an OUTER JOIN. Either the
150260 ** target slot in the parent is the right operand of a LEFT JOIN, or one of
150261 ** the left operands of a RIGHT JOIN. In either case, we need to potentially
150262 ** bypass the substituted expression with OP_IfNullRow.
150263 **
150264 ** Suppose the original expression is an integer constant. Even though the table
@@ -149987,21 +151092,16 @@
151092 ** elements we are now copying in.
151093 */
151094 pSub = pSub1;
151095 for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
151096 int nSubSrc;
151097 u8 jointype = pSubitem->fg.jointype;
 
151098 assert( pSub!=0 );
151099 pSubSrc = pSub->pSrc; /* FROM clause of subquery */
151100 nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
151101 pSrc = pParent->pSrc; /* FROM clause of the outer query */
151102
 
 
 
 
151103 /* The subquery uses a single slot of the FROM clause of the outer
151104 ** query. If the subquery has more than one element in its FROM clause,
151105 ** then expand the outer query to make space for it to hold all elements
151106 ** of the subquery.
151107 **
@@ -150017,10 +151117,11 @@
151117 */
151118 if( nSubSrc>1 ){
151119 pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1);
151120 if( pSrc==0 ) break;
151121 pParent->pSrc = pSrc;
151122 pSubitem = &pSrc->a[iFrom];
151123 }
151124
151125 /* Transfer the FROM clause terms from the subquery into the
151126 ** outer query.
151127 */
@@ -150031,15 +151132,14 @@
151132 assert( pItem->fg.isSubquery
151133 || pItem->fg.fixedSchema
151134 || pItem->u4.zDatabase==0 );
151135 if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
151136 *pItem = pSubSrc->a[i];
151137 pItem->fg.jointype |= (jointype & JT_LTORJ);
151138 memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
151139 }
151140 pSubitem->fg.jointype |= jointype;
 
151141
151142 /* Now begin substituting subquery result set expressions for
151143 ** references to the iParent in the outer query.
151144 **
151145 ** Example:
@@ -152746,10 +153846,11 @@
153846 Select *pSub = pWhere->x.pSelect;
153847 Expr *pSubWhere = pSub->pWhere;
153848 if( pSub->pSrc->nSrc==1
153849 && (pSub->selFlags & SF_Aggregate)==0
153850 && !pSub->pSrc->a[0].fg.isSubquery
153851 && pSub->pLimit==0
153852 ){
153853 memset(pWhere, 0, sizeof(*pWhere));
153854 pWhere->op = TK_INTEGER;
153855 pWhere->u.iValue = 1;
153856 ExprSetProperty(pWhere, EP_IntValue);
@@ -152774,10 +153875,119 @@
153875 existsToJoin(pParse, p, pSubWhere);
153876 }
153877 }
153878 }
153879 }
153880
153881 /*
153882 ** Type used for Walker callbacks by selectCheckOnClauses().
153883 */
153884 typedef struct CheckOnCtx CheckOnCtx;
153885 struct CheckOnCtx {
153886 SrcList *pSrc; /* SrcList for this context */
153887 int iJoin; /* Cursor numbers must be =< than this */
153888 CheckOnCtx *pParent; /* Parent context */
153889 };
153890
153891 /*
153892 ** True if the SrcList passed as the only argument contains at least
153893 ** one RIGHT or FULL JOIN. False otherwise.
153894 */
153895 #define hasRightJoin(pSrc) (((pSrc)->a[0].fg.jointype & JT_LTORJ)!=0)
153896
153897 /*
153898 ** The xExpr callback for the search of invalid ON clause terms.
153899 */
153900 static int selectCheckOnClausesExpr(Walker *pWalker, Expr *pExpr){
153901 CheckOnCtx *pCtx = pWalker->u.pCheckOnCtx;
153902
153903 /* Check if pExpr is root or near-root of an ON clause constraint that needs
153904 ** to be checked to ensure that it does not refer to tables in its FROM
153905 ** clause to the right of itself. i.e. it is either:
153906 **
153907 ** + an ON clause on an OUTER join, or
153908 ** + an ON clause on an INNER join within a FROM that features at
153909 ** least one RIGHT or FULL join.
153910 */
153911 if( (ExprHasProperty(pExpr, EP_OuterON))
153912 || (ExprHasProperty(pExpr, EP_InnerON) && hasRightJoin(pCtx->pSrc))
153913 ){
153914 /* If CheckOnCtx.iJoin is already set, then fall through and process
153915 ** this expression node as normal. Or, if CheckOnCtx.iJoin is still 0,
153916 ** set it to the cursor number of the RHS of the join to which this
153917 ** ON expression was attached and then iterate through the entire
153918 ** expression. */
153919 assert( pCtx->iJoin==0 || pCtx->iJoin==pExpr->w.iJoin );
153920 if( pCtx->iJoin==0 ){
153921 pCtx->iJoin = pExpr->w.iJoin;
153922 sqlite3WalkExprNN(pWalker, pExpr);
153923 pCtx->iJoin = 0;
153924 return WRC_Prune;
153925 }
153926 }
153927
153928 if( pExpr->op==TK_COLUMN ){
153929 /* A column expression. Find the SrcList (if any) to which it refers.
153930 ** Then, if CheckOnCtx.iJoin indicates that this expression is part of an
153931 ** ON clause from that SrcList (i.e. if iJoin is non-zero), check that it
153932 ** does not refer to a table to the right of CheckOnCtx.iJoin. */
153933 do {
153934 SrcList *pSrc = pCtx->pSrc;
153935 int iTab = pExpr->iTable;
153936 if( iTab>=pSrc->a[0].iCursor && iTab<=pSrc->a[pSrc->nSrc-1].iCursor ){
153937 if( pCtx->iJoin && iTab>pCtx->iJoin ){
153938 sqlite3ErrorMsg(pWalker->pParse,
153939 "ON clause references tables to its right");
153940 return WRC_Abort;
153941 }
153942 break;
153943 }
153944 pCtx = pCtx->pParent;
153945 }while( pCtx );
153946 }
153947 return WRC_Continue;
153948 }
153949
153950 /*
153951 ** The xSelect callback for the search of invalid ON clause terms.
153952 */
153953 static int selectCheckOnClausesSelect(Walker *pWalker, Select *pSelect){
153954 CheckOnCtx *pCtx = pWalker->u.pCheckOnCtx;
153955 if( pSelect->pSrc==pCtx->pSrc || pSelect->pSrc->nSrc==0 ){
153956 return WRC_Continue;
153957 }else{
153958 CheckOnCtx sCtx;
153959 memset(&sCtx, 0, sizeof(sCtx));
153960 sCtx.pSrc = pSelect->pSrc;
153961 sCtx.pParent = pCtx;
153962 pWalker->u.pCheckOnCtx = &sCtx;
153963 sqlite3WalkSelect(pWalker, pSelect);
153964 pWalker->u.pCheckOnCtx = pCtx;
153965 pSelect->selFlags &= ~SF_OnToWhere;
153966 return WRC_Prune;
153967 }
153968 }
153969
153970 /*
153971 ** Check all ON clauses in pSelect to verify that they do not reference
153972 ** columns to the right.
153973 */
153974 static void selectCheckOnClauses(Parse *pParse, Select *pSelect){
153975 Walker w;
153976 CheckOnCtx sCtx;
153977 assert( pSelect->selFlags & SF_OnToWhere );
153978 assert( pSelect->pSrc!=0 && pSelect->pSrc->nSrc>=2 );
153979 memset(&w, 0, sizeof(w));
153980 w.pParse = pParse;
153981 w.xExprCallback = selectCheckOnClausesExpr;
153982 w.xSelectCallback = selectCheckOnClausesSelect;
153983 w.u.pCheckOnCtx = &sCtx;
153984 memset(&sCtx, 0, sizeof(sCtx));
153985 sCtx.pSrc = pSelect->pSrc;
153986 sqlite3WalkExprNN(&w, pSelect->pWhere);
153987 pSelect->selFlags &= ~SF_OnToWhere;
153988 }
153989
153990 /*
153991 ** Generate byte-code for the SELECT statement given in the p argument.
153992 **
153993 ** The results are returned according to the SelectDest structure.
@@ -152901,10 +154111,22 @@
154111 if( sqlite3TreeTrace & 0x10 ){
154112 TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
154113 sqlite3TreeViewSelect(0, p, 0);
154114 }
154115 #endif
154116
154117 /* If the SELECT statement contains ON clauses that were moved into
154118 ** the WHERE clause, go through and verify that none of the terms
154119 ** in the ON clauses reference tables to the right of the ON clause.
154120 ** Do this now, after name resolution, but before query flattening
154121 */
154122 if( p->selFlags & SF_OnToWhere ){
154123 selectCheckOnClauses(pParse, p);
154124 if( pParse->nErr ){
154125 goto select_end;
154126 }
154127 }
154128
154129 /* If the SF_UFSrcCheck flag is set, then this function is being called
154130 ** as part of populating the temp table for an UPDATE...FROM statement.
154131 ** In this case, it is an error if the target object (pSrc->a[0]) name
154132 ** or alias is duplicated within FROM clause (pSrc->a[1..n]).
@@ -153766,10 +154988,11 @@
154988 iBMem = pParse->nMem + 1;
154989 pParse->nMem += pGroupBy->nExpr;
154990 sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
154991 VdbeComment((v, "clear abort flag"));
154992 sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
154993 sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
154994
154995 /* Begin a loop that will extract all source rows in GROUP BY order.
154996 ** This might involve two separate loops with an OP_Sort in between, or
154997 ** it might be a single loop that uses an index to extract information
154998 ** in the right order to begin with.
@@ -155466,11 +156689,14 @@
156689 sqlite3 *db = pParse->db;
156690 ExprList *pNew;
156691 Returning *pReturning;
156692 Select sSelect;
156693 SrcList *pFrom;
156694 union {
156695 SrcList sSrc;
156696 u8 fromSpace[SZ_SRCLIST_1];
156697 } uSrc;
156698
156699 assert( v!=0 );
156700 if( !pParse->bReturning ){
156701 /* This RETURNING trigger must be for a different statement as
156702 ** this statement lacks a RETURNING clause. */
@@ -155482,12 +156708,12 @@
156708 if( pTrigger != &(pReturning->retTrig) ){
156709 /* This RETURNING trigger is for a different statement */
156710 return;
156711 }
156712 memset(&sSelect, 0, sizeof(sSelect));
156713 memset(&uSrc, 0, sizeof(uSrc));
156714 pFrom = &uSrc.sSrc;
156715 sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
156716 sSelect.pSrc = pFrom;
156717 pFrom->nSrc = 1;
156718 pFrom->a[0].pSTab = pTab;
156719 pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
@@ -163014,11 +164240,14 @@
164240 WhereClause *pWC = &pWInfo->sWC;
164241 WhereInfo *pSubWInfo;
164242 WhereLoop *pLoop = pLevel->pWLoop;
164243 SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
164244 SrcList *pFrom;
164245 union {
164246 SrcList sSrc;
164247 u8 fromSpace[SZ_SRCLIST_1];
164248 } uSrc;
164249 Bitmask mAll = 0;
164250 int k;
164251
164252 ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
164253 sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -163058,11 +164287,11 @@
164287 if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
164288 pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
164289 sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
164290 }
164291 }
164292 pFrom = &uSrc.sSrc;
164293 pFrom->nSrc = 1;
164294 pFrom->nAlloc = 1;
164295 memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
164296 pFrom->a[0].fg.jointype = 0;
164297 assert( pParse->withinRJSubrtn < 100 );
@@ -164275,25 +165504,11 @@
165504 Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
165505 if( ExprHasProperty(pExpr, EP_OuterON) ){
165506 prereqAll |= x;
165507 extraRight = x-1; /* ON clause terms may not be used with an index
165508 ** on left table of a LEFT JOIN. Ticket #3015 */
 
 
 
 
165509 }else if( (prereqAll>>1)>=x ){
 
 
 
 
 
 
 
 
 
 
165510 ExprClearProperty(pExpr, EP_InnerON);
165511 }
165512 }
165513 pTerm->prereqAll = prereqAll;
165514 pTerm->leftCursor = -1;
@@ -169084,10 +170299,11 @@
170299 if( pProbe->bNoQuery ) continue;
170300 rSize = pProbe->aiRowLogEst[0];
170301 pNew->u.btree.nEq = 0;
170302 pNew->u.btree.nBtm = 0;
170303 pNew->u.btree.nTop = 0;
170304 pNew->u.btree.nDistinctCol = 0;
170305 pNew->nSkip = 0;
170306 pNew->nLTerm = 0;
170307 pNew->iSortIdx = 0;
170308 pNew->rSetup = 0;
170309 pNew->prereq = mPrereq;
@@ -170150,14 +171366,16 @@
171366 if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
171367 if( pLoop->u.vtab.isOrdered
171368 && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
171369 ){
171370 obSat = obDone;
171371 }else{
171372 /* No further ORDER BY terms may be matched. So this call should
171373 ** return >=0, not -1. Clear isOrderDistinct to ensure it does so. */
171374 isOrderDistinct = 0;
171375 }
171376 break;
 
 
171377 }
171378 iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
171379
171380 /* Mark off any ORDER BY term X that is a column in the table of
171381 ** the current loop for which there is term in the WHERE
@@ -170897,21 +172115,28 @@
172115
172116 /* Check to see if pWLoop should be added to the set of
172117 ** mxChoice best-so-far paths.
172118 **
172119 ** First look for an existing path among best-so-far paths
172120 ** that:
172121 ** (1) covers the same set of loops, and
172122 ** (2) has a compatible isOrdered value.
172123 **
172124 ** "Compatible isOrdered value" means either
172125 ** (A) both have isOrdered==-1, or
172126 ** (B) both have isOrder>=0, or
172127 ** (C) ordering does not matter because this is the last round
172128 ** of the solver.
172129 **
172130 ** The term "((pTo->isOrdered^isOrdered)&0x80)==0" is equivalent
172131 ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range
172132 ** of legal values for isOrdered, -1..64.
172133 */
172134 testcase( nTo==0 );
172135 for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
172136 if( pTo->maskLoop==maskNew
172137 && ( ((pTo->isOrdered^isOrdered)&0x80)==0 || iLoop==nLoop-1 )
172138 ){
172139 testcase( jj==nTo-1 );
172140 break;
172141 }
172142 }
@@ -171062,15 +172287,14 @@
172287 sqlite3ErrorMsg(pParse, "no query solution");
172288 sqlite3StackFreeNN(pParse->db, pSpace);
172289 return SQLITE_ERROR;
172290 }
172291
172292 /* Only one path is available, which is the best path */
172293 assert( nFrom==1 );
172294 pFrom = aFrom;
172295
 
 
172296 assert( pWInfo->nLevel==nLoop );
172297 /* Load the lowest cost path into pWInfo */
172298 for(iLoop=0; iLoop<nLoop; iLoop++){
172299 WhereLevel *pLevel = pWInfo->a + iLoop;
172300 pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
@@ -171199,11 +172423,14 @@
172423 int once = 0;
172424 #endif
172425 for(i=0; i<pWInfo->nLevel; i++){
172426 WhereLoop *p = pWInfo->a[i].pWLoop;
172427 if( p==0 ) break;
172428 if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
172429 /* Treat a vtab scan as similar to a full-table scan */
172430 break;
172431 }
172432 if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){
172433 u8 iTab = p->iTab;
172434 WhereLoop *pLoop;
172435 for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){
172436 if( pLoop->iTab!=iTab ) continue;
@@ -175312,11 +176539,11 @@
176539 ** RETURN_ROW
176540 **
176541 **
176542 ** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
176543 **
176544 ** ... loop started by sqlite3WhereBegin() ...
176545 ** if( new partition ){
176546 ** Gosub flush
176547 ** }
176548 ** Insert new row into eph table.
176549 ** if( first row of partition ){
@@ -175830,10 +177057,16 @@
177057 addrStart = sqlite3VdbeCurrentAddr(v);
177058 addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
177059 addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
177060 }else{
177061 assert( pMWin->eEnd==TK_FOLLOWING );
177062 /* assert( regStart>=0 );
177063 ** regEnd = regEnd - regStart;
177064 ** regStart = 0; */
177065 sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd);
177066 sqlite3VdbeAddOp2(v, OP_Integer, 0, regStart);
177067
177068 addrStart = sqlite3VdbeCurrentAddr(v);
177069 addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
177070 addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
177071 }
177072 sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
@@ -183317,13 +184550,10 @@
184550 #endif
184551 #ifdef SQLITE_ENABLE_DBSTAT_VTAB
184552 sqlite3DbstatRegister,
184553 #endif
184554 sqlite3TestExtInit,
 
 
 
184555 #ifdef SQLITE_ENABLE_STMTVTAB
184556 sqlite3StmtVtabInit,
184557 #endif
184558 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
184559 sqlite3VdbeBytecodeVtabInit,
@@ -184775,10 +186005,13 @@
186005 for(i=0; i<2 && zName==0; i++, rc &= 0xff){
186006 switch( rc ){
186007 case SQLITE_OK: zName = "SQLITE_OK"; break;
186008 case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
186009 case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
186010 case SQLITE_ERROR_RETRY: zName = "SQLITE_ERROR_RETRY"; break;
186011 case SQLITE_ERROR_MISSING_COLLSEQ:
186012 zName = "SQLITE_ERROR_MISSING_COLLSEQ"; break;
186013 case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
186014 case SQLITE_PERM: zName = "SQLITE_PERM"; break;
186015 case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
186016 case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
186017 case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
@@ -185955,10 +187188,33 @@
187188 }
187189 }
187190 sqlite3_mutex_leave(db->mutex);
187191 return z;
187192 }
187193
187194 /*
187195 ** Set the error code and error message associated with the database handle.
187196 **
187197 ** This routine is intended to be called by outside extensions (ex: the
187198 ** Session extension). Internal logic should invoke sqlite3Error() or
187199 ** sqlite3ErrorWithMsg() directly.
187200 */
187201 SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zMsg){
187202 int rc = SQLITE_OK;
187203 if( !sqlite3SafetyCheckSickOrOk(db) ){
187204 return SQLITE_MISUSE_BKPT;
187205 }
187206 sqlite3_mutex_enter(db->mutex);
187207 if( zMsg ){
187208 sqlite3ErrorWithMsg(db, errcode, "%s", zMsg);
187209 }else{
187210 sqlite3Error(db, errcode);
187211 }
187212 rc = sqlite3ApiExit(db, rc);
187213 sqlite3_mutex_leave(db->mutex);
187214 return rc;
187215 }
187216
187217 /*
187218 ** Return the byte offset of the most recent error
187219 */
187220 SQLITE_API int sqlite3_error_offset(sqlite3 *db){
@@ -187780,17 +189036,19 @@
189036 case SQLITE_TESTCTRL_ISINIT: {
189037 if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
189038 break;
189039 }
189040
189041 /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, mode, tnum);
189042 **
189043 ** This test control is used to create imposter tables. "db" is a pointer
189044 ** to the database connection. dbName is the database name (ex: "main" or
189045 ** "temp") which will receive the imposter. "mode" turns imposter mode on
189046 ** or off. mode==0 means imposter mode is off. mode==1 means imposter mode
189047 ** is on. mode==2 means imposter mode is on but results in an imposter
189048 ** table that is read-only unless writable_schema is on. "tnum" is the
189049 ** root page of the b-tree to which the imposter table should connect.
189050 **
189051 ** Enable imposter mode only when the schema has already been parsed. Then
189052 ** run a single CREATE TABLE statement to construct the imposter table in
189053 ** the parsed schema. Then turn imposter mode back off again.
189054 **
@@ -189023,21 +190281,24 @@
190281 **
190282 */
190283 #ifndef _FTSINT_H
190284 #define _FTSINT_H
190285
190286 /*
190287 ** Activate assert() only if SQLITE_TEST is enabled.
190288 */
190289 #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
190290 # define NDEBUG 1
190291 #endif
190292
190293 /* #include <assert.h> */
190294 /* #include <stdlib.h> */
190295 /* #include <stddef.h> */
190296 /* #include <stdio.h> */
190297 /* #include <string.h> */
190298 /* #include <stdarg.h> */
190299
 
 
 
 
190300 /* FTS3/FTS4 require virtual tables */
190301 #ifdef SQLITE_OMIT_VIRTUALTABLE
190302 # undef SQLITE_ENABLE_FTS3
190303 # undef SQLITE_ENABLE_FTS4
190304 #endif
@@ -189476,17 +190737,10 @@
190737 /*
190738 ** Macro used to suppress compiler warnings for unused parameters.
190739 */
190740 #define UNUSED_PARAMETER(x) (void)(x)
190741
 
 
 
 
 
 
 
190742 /*
190743 ** The TESTONLY macro is used to enclose variable declarations or
190744 ** other bits of code that are needed to support the arguments
190745 ** within testcase() and assert() macros.
190746 */
@@ -203757,12 +205011,12 @@
205011 /*
205012 ** An object of this type contains the state required to create or append
205013 ** to an appendable b-tree segment.
205014 */
205015 struct IncrmergeWriter {
205016 i64 nLeafEst; /* Space allocated for leaf blocks */
205017 i64 nWork; /* Number of leaf pages flushed */
205018 sqlite3_int64 iAbsLevel; /* Absolute level of input segments */
205019 int iIdx; /* Index of *output* segment in iAbsLevel+1 */
205020 sqlite3_int64 iStart; /* Block number of first allocated block */
205021 sqlite3_int64 iEnd; /* Block number of last allocated block */
205022 sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
@@ -204504,21 +205758,21 @@
205758 Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */
205759 IncrmergeWriter *pWriter /* Populate this object */
205760 ){
205761 int rc; /* Return Code */
205762 int i; /* Iterator variable */
205763 i64 nLeafEst = 0; /* Blocks allocated for leaf nodes */
205764 sqlite3_stmt *pLeafEst = 0; /* SQL used to determine nLeafEst */
205765 sqlite3_stmt *pFirstBlock = 0; /* SQL used to determine first block */
205766
205767 /* Calculate nLeafEst. */
205768 rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0);
205769 if( rc==SQLITE_OK ){
205770 sqlite3_bind_int64(pLeafEst, 1, iAbsLevel);
205771 sqlite3_bind_int64(pLeafEst, 2, pCsr->nSegment);
205772 if( SQLITE_ROW==sqlite3_step(pLeafEst) ){
205773 nLeafEst = sqlite3_column_int64(pLeafEst, 0);
205774 }
205775 rc = sqlite3_reset(pLeafEst);
205776 }
205777 if( rc!=SQLITE_OK ) return rc;
205778
@@ -205897,14 +207151,10 @@
207151 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
207152
207153 /* #include <string.h> */
207154 /* #include <assert.h> */
207155
 
 
 
 
207156 /*
207157 ** Characters that may appear in the second argument to matchinfo().
207158 */
207159 #define FTS3_MATCHINFO_NPHRASE 'p' /* 1 value */
207160 #define FTS3_MATCHINFO_NCOL 'c' /* 1 value */
@@ -210754,11 +212004,11 @@
212004 switch( (u8)zIn[1] ){
212005 case '\'':
212006 jsonAppendChar(pOut, '\'');
212007 break;
212008 case 'v':
212009 jsonAppendRawNZ(pOut, "\\u000b", 6);
212010 break;
212011 case 'x':
212012 if( sz2<4 ){
212013 pOut->eErr |= JSTRING_MALFORMED;
212014 sz2 = 2;
@@ -211604,23 +212854,31 @@
212854 /*
212855 ** Return the value of the BLOB node at index i.
212856 **
212857 ** If the value is a primitive, return it as an SQL value.
212858 ** If the value is an array or object, return it as either
212859 ** JSON text or the BLOB encoding, depending on the eMode flag
212860 ** as follows:
212861 **
212862 ** eMode==0 JSONB if the JSON_B flag is set in userdata or
212863 ** text if the JSON_B flag is omitted from userdata.
212864 **
212865 ** eMode==1 Text
212866 **
212867 ** eMode==2 JSONB
212868 */
212869 static void jsonReturnFromBlob(
212870 JsonParse *pParse, /* Complete JSON parse tree */
212871 u32 i, /* Index of the node */
212872 sqlite3_context *pCtx, /* Return value for this function */
212873 int eMode /* Format of return: text of JSONB */
212874 ){
212875 u32 n, sz;
212876 int rc;
212877 sqlite3 *db = sqlite3_context_db_handle(pCtx);
212878
212879 assert( eMode>=0 && eMode<=2 );
212880 n = jsonbPayloadSize(pParse, i, &sz);
212881 if( n==0 ){
212882 sqlite3_result_error(pCtx, "malformed JSON", -1);
212883 return;
212884 }
@@ -211657,11 +212915,23 @@
212915 z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
212916 if( z==0 ) goto returnfromblob_oom;
212917 rc = sqlite3DecOrHexToI64(z, &iRes);
212918 sqlite3DbFree(db, z);
212919 if( rc==0 ){
212920 if( iRes<0 ){
212921 /* A hexadecimal literal with 16 significant digits and with the
212922 ** high-order bit set is a negative integer in SQLite (and hence
212923 ** iRes comes back as negative) but should be interpreted as a
212924 ** positive value if it occurs within JSON. The value is too
212925 ** large to appear as an SQLite integer so it must be converted
212926 ** into floating point. */
212927 double r;
212928 r = (double)*(sqlite3_uint64*)&iRes;
212929 sqlite3_result_double(pCtx, bNeg ? -r : r);
212930 }else{
212931 sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
212932 }
212933 }else if( rc==3 && bNeg ){
212934 sqlite3_result_int64(pCtx, SMALLEST_INT64);
212935 }else if( rc==1 ){
212936 goto returnfromblob_malformed;
212937 }else{
@@ -211735,12 +213005,18 @@
213005 sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
213006 break;
213007 }
213008 case JSONB_ARRAY:
213009 case JSONB_OBJECT: {
213010 if( eMode==0 ){
213011 if( (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)) & JSON_BLOB)!=0 ){
213012 eMode = 2;
213013 }else{
213014 eMode = 1;
213015 }
213016 }
213017 if( eMode==2 ){
213018 sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
213019 }else{
213020 jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
213021 }
213022 break;
@@ -213383,10 +214659,11 @@
214659 u32 i; /* Index in sParse.aBlob[] of current row */
214660 u32 iEnd; /* EOF when i equals or exceeds this value */
214661 u32 nRoot; /* Size of the root path in bytes */
214662 u8 eType; /* Type of the container for element i */
214663 u8 bRecursive; /* True for json_tree(). False for json_each() */
214664 u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */
214665 u32 nParent; /* Current nesting depth */
214666 u32 nParentAlloc; /* Space allocated for aParent[] */
214667 JsonParent *aParent; /* Parent elements of i */
214668 sqlite3 *db; /* Database connection */
214669 JsonString path; /* Current path */
@@ -213394,10 +214671,12 @@
214671 };
214672 typedef struct JsonEachConnection JsonEachConnection;
214673 struct JsonEachConnection {
214674 sqlite3_vtab base; /* Base class - must be first */
214675 sqlite3 *db; /* Database connection */
214676 u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */
214677 u8 bRecursive; /* True for json_tree(). False for json_each() */
214678 };
214679
214680
214681 /* Constructor for the json_each virtual table */
214682 static int jsonEachConnect(
@@ -213436,10 +214715,12 @@
214715 pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
214716 *ppVtab = (sqlite3_vtab*)pNew;
214717 if( pNew==0 ) return SQLITE_NOMEM;
214718 sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
214719 pNew->db = db;
214720 pNew->eMode = argv[0][4]=='b' ? 2 : 1;
214721 pNew->bRecursive = argv[0][4+pNew->eMode]=='t';
214722 }
214723 return rc;
214724 }
214725
214726 /* destructor for json_each virtual table */
@@ -213447,34 +214728,26 @@
214728 JsonEachConnection *p = (JsonEachConnection*)pVtab;
214729 sqlite3DbFree(p->db, pVtab);
214730 return SQLITE_OK;
214731 }
214732
214733 /* constructor for a JsonEachCursor object for json_each()/json_tree(). */
214734 static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
214735 JsonEachConnection *pVtab = (JsonEachConnection*)p;
214736 JsonEachCursor *pCur;
214737
214738 UNUSED_PARAMETER(p);
214739 pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
214740 if( pCur==0 ) return SQLITE_NOMEM;
214741 pCur->db = pVtab->db;
214742 pCur->eMode = pVtab->eMode;
214743 pCur->bRecursive = pVtab->bRecursive;
214744 jsonStringZero(&pCur->path);
214745 *ppCursor = &pCur->base;
214746 return SQLITE_OK;
214747 }
214748
 
 
 
 
 
 
 
 
 
 
214749 /* Reset a JsonEachCursor back to its original state. Free any memory
214750 ** held. */
214751 static void jsonEachCursorReset(JsonEachCursor *p){
214752 jsonParseReset(&p->sParse);
214753 jsonStringReset(&p->path);
@@ -213675,11 +214948,11 @@
214948 }
214949 break;
214950 }
214951 case JEACH_VALUE: {
214952 u32 i = jsonSkipLabel(p);
214953 jsonReturnFromBlob(&p->sParse, i, ctx, p->eMode);
214954 if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
214955 sqlite3_result_subtype(ctx, JSON_SUBTYPE);
214956 }
214957 break;
214958 }
@@ -213919,40 +215192,11 @@
215192 0, /* xCreate */
215193 jsonEachConnect, /* xConnect */
215194 jsonEachBestIndex, /* xBestIndex */
215195 jsonEachDisconnect, /* xDisconnect */
215196 0, /* xDestroy */
215197 jsonEachOpen, /* xOpen - open a cursor */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215198 jsonEachClose, /* xClose - close a cursor */
215199 jsonEachFilter, /* xFilter - configure scan constraints */
215200 jsonEachNext, /* xNext - advance a cursor */
215201 jsonEachEof, /* xEof - check for end of scan */
215202 jsonEachColumn, /* xColumn - read data */
@@ -214037,26 +215281,25 @@
215281 #endif
215282 }
215283
215284 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
215285 /*
215286 ** Register the JSON table-valued function named zName and return a
215287 ** pointer to its Module object. Return NULL if something goes wrong.
215288 */
215289 SQLITE_PRIVATE Module *sqlite3JsonVtabRegister(sqlite3 *db, const char *zName){
 
 
 
 
 
 
 
 
215290 unsigned int i;
215291 static const char *azModule[] = {
215292 "json_each", "json_tree", "jsonb_each", "jsonb_tree"
215293 };
215294 assert( sqlite3HashFind(&db->aModule, zName)==0 );
215295 for(i=0; i<sizeof(azModule)/sizeof(azModule[0]); i++){
215296 if( sqlite3StrICmp(azModule[i],zName)==0 ){
215297 return sqlite3VtabCreateModule(db, azModule[i], &jsonEachModule, 0, 0);
215298 }
215299 }
215300 return 0;
215301 }
215302 #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) */
215303
215304 /************** End of json.c ************************************************/
215305 /************** Begin file rtree.c *******************************************/
@@ -228289,12 +229532,12 @@
229532 typedef struct DbpageTable DbpageTable;
229533 typedef struct DbpageCursor DbpageCursor;
229534
229535 struct DbpageCursor {
229536 sqlite3_vtab_cursor base; /* Base class. Must be first */
229537 Pgno pgno; /* Current page number */
229538 Pgno mxPgno; /* Last page to visit on this scan */
229539 Pager *pPager; /* Pager being read/written */
229540 DbPage *pPage1; /* Page 1 of the database */
229541 int iDb; /* Index of database to analyze */
229542 int szPage; /* Size of each page in bytes */
229543 };
@@ -228427,11 +229670,11 @@
229670 if( pCsr==0 ){
229671 return SQLITE_NOMEM_BKPT;
229672 }else{
229673 memset(pCsr, 0, sizeof(DbpageCursor));
229674 pCsr->base.pVtab = pVTab;
229675 pCsr->pgno = 0;
229676 }
229677
229678 *ppCursor = (sqlite3_vtab_cursor *)pCsr;
229679 return SQLITE_OK;
229680 }
@@ -228527,16 +229770,16 @@
229770 ){
229771 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
229772 int rc = SQLITE_OK;
229773 switch( i ){
229774 case 0: { /* pgno */
229775 sqlite3_result_int64(ctx, (sqlite3_int64)pCsr->pgno);
229776 break;
229777 }
229778 case 1: { /* data */
229779 DbPage *pDbPage = 0;
229780 if( pCsr->pgno==(Pgno)((PENDING_BYTE/pCsr->szPage)+1) ){
229781 /* The pending byte page. Assume it is zeroed out. Attempting to
229782 ** request this page from the page is an SQLITE_CORRUPT error. */
229783 sqlite3_result_zeroblob(ctx, pCsr->szPage);
229784 }else{
229785 rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
@@ -228606,14 +229849,14 @@
229849 if( argc==1 ){
229850 zErr = "cannot delete";
229851 goto update_fail;
229852 }
229853 if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
229854 pgno = (Pgno)sqlite3_value_int64(argv[2]);
229855 isInsert = 1;
229856 }else{
229857 pgno = (Pgno)sqlite3_value_int64(argv[0]);
229858 if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
229859 zErr = "cannot insert";
229860 goto update_fail;
229861 }
229862 isInsert = 0;
@@ -228744,10 +229987,539 @@
229987 #elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
229988 SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
229989 #endif /* SQLITE_ENABLE_DBSTAT_VTAB */
229990
229991 /************** End of dbpage.c **********************************************/
229992 /************** Begin file carray.c ******************************************/
229993 /*
229994 ** 2016-06-29
229995 **
229996 ** The author disclaims copyright to this source code. In place of
229997 ** a legal notice, here is a blessing:
229998 **
229999 ** May you do good and not evil.
230000 ** May you find forgiveness for yourself and forgive others.
230001 ** May you share freely, never taking more than you give.
230002 **
230003 *************************************************************************
230004 **
230005 ** This file implements a table-valued-function that
230006 ** returns the values in a C-language array.
230007 ** Examples:
230008 **
230009 ** SELECT * FROM carray($ptr,5)
230010 **
230011 ** The query above returns 5 integers contained in a C-language array
230012 ** at the address $ptr. $ptr is a pointer to the array of integers.
230013 ** The pointer value must be assigned to $ptr using the
230014 ** sqlite3_bind_pointer() interface with a pointer type of "carray".
230015 ** For example:
230016 **
230017 ** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
230018 ** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
230019 ** sqlite3_bind_pointer(pStmt, i, aX, "carray", 0);
230020 **
230021 ** There is an optional third parameter to determine the datatype of
230022 ** the C-language array. Allowed values of the third parameter are
230023 ** 'int32', 'int64', 'double', 'char*', 'struct iovec'. Example:
230024 **
230025 ** SELECT * FROM carray($ptr,10,'char*');
230026 **
230027 ** The default value of the third parameter is 'int32'.
230028 **
230029 ** HOW IT WORKS
230030 **
230031 ** The carray "function" is really a virtual table with the
230032 ** following schema:
230033 **
230034 ** CREATE TABLE carray(
230035 ** value,
230036 ** pointer HIDDEN,
230037 ** count HIDDEN,
230038 ** ctype TEXT HIDDEN
230039 ** );
230040 **
230041 ** If the hidden columns "pointer" and "count" are unconstrained, then
230042 ** the virtual table has no rows. Otherwise, the virtual table interprets
230043 ** the integer value of "pointer" as a pointer to the array and "count"
230044 ** as the number of elements in the array. The virtual table steps through
230045 ** the array, element by element.
230046 */
230047 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY)
230048 /* #include "sqliteInt.h" */
230049 #if defined(_WIN32) || defined(__RTP__) || defined(_WRS_KERNEL)
230050 struct iovec {
230051 void *iov_base;
230052 size_t iov_len;
230053 };
230054 #else
230055 # include <sys/uio.h>
230056 #endif
230057
230058 /*
230059 ** Names of allowed datatypes
230060 */
230061 static const char *azType[] = { "int32", "int64", "double", "char*",
230062 "struct iovec" };
230063
230064 /*
230065 ** Structure used to hold the sqlite3_carray_bind() information
230066 */
230067 typedef struct carray_bind carray_bind;
230068 struct carray_bind {
230069 void *aData; /* The data */
230070 int nData; /* Number of elements */
230071 int mFlags; /* Control flags */
230072 void (*xDel)(void*); /* Destructor for aData */
230073 };
230074
230075
230076 /* carray_cursor is a subclass of sqlite3_vtab_cursor which will
230077 ** serve as the underlying representation of a cursor that scans
230078 ** over rows of the result
230079 */
230080 typedef struct carray_cursor carray_cursor;
230081 struct carray_cursor {
230082 sqlite3_vtab_cursor base; /* Base class - must be first */
230083 sqlite3_int64 iRowid; /* The rowid */
230084 void *pPtr; /* Pointer to the array of values */
230085 sqlite3_int64 iCnt; /* Number of integers in the array */
230086 unsigned char eType; /* One of the CARRAY_type values */
230087 };
230088
230089 /*
230090 ** The carrayConnect() method is invoked to create a new
230091 ** carray_vtab that describes the carray virtual table.
230092 **
230093 ** Think of this routine as the constructor for carray_vtab objects.
230094 **
230095 ** All this routine needs to do is:
230096 **
230097 ** (1) Allocate the carray_vtab object and initialize all fields.
230098 **
230099 ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
230100 ** result set of queries against carray will look like.
230101 */
230102 static int carrayConnect(
230103 sqlite3 *db,
230104 void *pAux,
230105 int argc, const char *const*argv,
230106 sqlite3_vtab **ppVtab,
230107 char **pzErr
230108 ){
230109 sqlite3_vtab *pNew;
230110 int rc;
230111
230112 /* Column numbers */
230113 #define CARRAY_COLUMN_VALUE 0
230114 #define CARRAY_COLUMN_POINTER 1
230115 #define CARRAY_COLUMN_COUNT 2
230116 #define CARRAY_COLUMN_CTYPE 3
230117
230118 rc = sqlite3_declare_vtab(db,
230119 "CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
230120 if( rc==SQLITE_OK ){
230121 pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
230122 if( pNew==0 ) return SQLITE_NOMEM;
230123 memset(pNew, 0, sizeof(*pNew));
230124 }
230125 return rc;
230126 }
230127
230128 /*
230129 ** This method is the destructor for carray_cursor objects.
230130 */
230131 static int carrayDisconnect(sqlite3_vtab *pVtab){
230132 sqlite3_free(pVtab);
230133 return SQLITE_OK;
230134 }
230135
230136 /*
230137 ** Constructor for a new carray_cursor object.
230138 */
230139 static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
230140 carray_cursor *pCur;
230141 pCur = sqlite3_malloc( sizeof(*pCur) );
230142 if( pCur==0 ) return SQLITE_NOMEM;
230143 memset(pCur, 0, sizeof(*pCur));
230144 *ppCursor = &pCur->base;
230145 return SQLITE_OK;
230146 }
230147
230148 /*
230149 ** Destructor for a carray_cursor.
230150 */
230151 static int carrayClose(sqlite3_vtab_cursor *cur){
230152 sqlite3_free(cur);
230153 return SQLITE_OK;
230154 }
230155
230156
230157 /*
230158 ** Advance a carray_cursor to its next row of output.
230159 */
230160 static int carrayNext(sqlite3_vtab_cursor *cur){
230161 carray_cursor *pCur = (carray_cursor*)cur;
230162 pCur->iRowid++;
230163 return SQLITE_OK;
230164 }
230165
230166 /*
230167 ** Return values of columns for the row at which the carray_cursor
230168 ** is currently pointing.
230169 */
230170 static int carrayColumn(
230171 sqlite3_vtab_cursor *cur, /* The cursor */
230172 sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
230173 int i /* Which column to return */
230174 ){
230175 carray_cursor *pCur = (carray_cursor*)cur;
230176 sqlite3_int64 x = 0;
230177 switch( i ){
230178 case CARRAY_COLUMN_POINTER: return SQLITE_OK;
230179 case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
230180 case CARRAY_COLUMN_CTYPE: {
230181 sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
230182 return SQLITE_OK;
230183 }
230184 default: {
230185 switch( pCur->eType ){
230186 case CARRAY_INT32: {
230187 int *p = (int*)pCur->pPtr;
230188 sqlite3_result_int(ctx, p[pCur->iRowid-1]);
230189 return SQLITE_OK;
230190 }
230191 case CARRAY_INT64: {
230192 sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr;
230193 sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
230194 return SQLITE_OK;
230195 }
230196 case CARRAY_DOUBLE: {
230197 double *p = (double*)pCur->pPtr;
230198 sqlite3_result_double(ctx, p[pCur->iRowid-1]);
230199 return SQLITE_OK;
230200 }
230201 case CARRAY_TEXT: {
230202 const char **p = (const char**)pCur->pPtr;
230203 sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
230204 return SQLITE_OK;
230205 }
230206 default: {
230207 const struct iovec *p = (struct iovec*)pCur->pPtr;
230208 assert( pCur->eType==CARRAY_BLOB );
230209 sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base,
230210 (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT);
230211 return SQLITE_OK;
230212 }
230213 }
230214 }
230215 }
230216 sqlite3_result_int64(ctx, x);
230217 return SQLITE_OK;
230218 }
230219
230220 /*
230221 ** Return the rowid for the current row. In this implementation, the
230222 ** rowid is the same as the output value.
230223 */
230224 static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
230225 carray_cursor *pCur = (carray_cursor*)cur;
230226 *pRowid = pCur->iRowid;
230227 return SQLITE_OK;
230228 }
230229
230230 /*
230231 ** Return TRUE if the cursor has been moved off of the last
230232 ** row of output.
230233 */
230234 static int carrayEof(sqlite3_vtab_cursor *cur){
230235 carray_cursor *pCur = (carray_cursor*)cur;
230236 return pCur->iRowid>pCur->iCnt;
230237 }
230238
230239 /*
230240 ** This method is called to "rewind" the carray_cursor object back
230241 ** to the first row of output.
230242 */
230243 static int carrayFilter(
230244 sqlite3_vtab_cursor *pVtabCursor,
230245 int idxNum, const char *idxStr,
230246 int argc, sqlite3_value **argv
230247 ){
230248 carray_cursor *pCur = (carray_cursor *)pVtabCursor;
230249 pCur->pPtr = 0;
230250 pCur->iCnt = 0;
230251 switch( idxNum ){
230252 case 1: {
230253 carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
230254 if( pBind==0 ) break;
230255 pCur->pPtr = pBind->aData;
230256 pCur->iCnt = pBind->nData;
230257 pCur->eType = pBind->mFlags & 0x07;
230258 break;
230259 }
230260 case 2:
230261 case 3: {
230262 pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
230263 pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
230264 if( idxNum<3 ){
230265 pCur->eType = CARRAY_INT32;
230266 }else{
230267 unsigned char i;
230268 const char *zType = (const char*)sqlite3_value_text(argv[2]);
230269 for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
230270 if( sqlite3_stricmp(zType, azType[i])==0 ) break;
230271 }
230272 if( i>=sizeof(azType)/sizeof(azType[0]) ){
230273 pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
230274 "unknown datatype: %Q", zType);
230275 return SQLITE_ERROR;
230276 }else{
230277 pCur->eType = i;
230278 }
230279 }
230280 break;
230281 }
230282 }
230283 pCur->iRowid = 1;
230284 return SQLITE_OK;
230285 }
230286
230287 /*
230288 ** SQLite will invoke this method one or more times while planning a query
230289 ** that uses the carray virtual table. This routine needs to create
230290 ** a query plan for each invocation and compute an estimated cost for that
230291 ** plan.
230292 **
230293 ** In this implementation idxNum is used to represent the
230294 ** query plan. idxStr is unused.
230295 **
230296 ** idxNum is:
230297 **
230298 ** 1 If only the pointer= constraint exists. In this case, the
230299 ** parameter must be bound using sqlite3_carray_bind().
230300 **
230301 ** 2 if the pointer= and count= constraints exist.
230302 **
230303 ** 3 if the ctype= constraint also exists.
230304 **
230305 ** idxNum is 0 otherwise and carray becomes an empty table.
230306 */
230307 static int carrayBestIndex(
230308 sqlite3_vtab *tab,
230309 sqlite3_index_info *pIdxInfo
230310 ){
230311 int i; /* Loop over constraints */
230312 int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
230313 int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
230314 int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
230315 unsigned seen = 0; /* Bitmask of == constrainted columns */
230316
230317 const struct sqlite3_index_constraint *pConstraint;
230318 pConstraint = pIdxInfo->aConstraint;
230319 for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
230320 if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
230321 if( pConstraint->iColumn>=0 ) seen |= 1 << pConstraint->iColumn;
230322 if( pConstraint->usable==0 ) continue;
230323 switch( pConstraint->iColumn ){
230324 case CARRAY_COLUMN_POINTER:
230325 ptrIdx = i;
230326 break;
230327 case CARRAY_COLUMN_COUNT:
230328 cntIdx = i;
230329 break;
230330 case CARRAY_COLUMN_CTYPE:
230331 ctypeIdx = i;
230332 break;
230333 }
230334 }
230335 if( ptrIdx>=0 ){
230336 pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
230337 pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
230338 pIdxInfo->estimatedCost = (double)1;
230339 pIdxInfo->estimatedRows = 100;
230340 pIdxInfo->idxNum = 1;
230341 if( cntIdx>=0 ){
230342 pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
230343 pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
230344 pIdxInfo->idxNum = 2;
230345 if( ctypeIdx>=0 ){
230346 pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
230347 pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
230348 pIdxInfo->idxNum = 3;
230349 }else if( seen & (1<<CARRAY_COLUMN_CTYPE) ){
230350 /* In a three-argument carray(), we need to know the value of all
230351 ** three arguments */
230352 return SQLITE_CONSTRAINT;
230353 }
230354 }else if( seen & (1<<CARRAY_COLUMN_COUNT) ){
230355 /* In a two-argument carray(), we need to know the value of both
230356 ** arguments */
230357 return SQLITE_CONSTRAINT;
230358 }
230359 }else{
230360 pIdxInfo->estimatedCost = (double)2147483647;
230361 pIdxInfo->estimatedRows = 2147483647;
230362 pIdxInfo->idxNum = 0;
230363 }
230364 return SQLITE_OK;
230365 }
230366
230367 /*
230368 ** This following structure defines all the methods for the
230369 ** carray virtual table.
230370 */
230371 static sqlite3_module carrayModule = {
230372 0, /* iVersion */
230373 0, /* xCreate */
230374 carrayConnect, /* xConnect */
230375 carrayBestIndex, /* xBestIndex */
230376 carrayDisconnect, /* xDisconnect */
230377 0, /* xDestroy */
230378 carrayOpen, /* xOpen - open a cursor */
230379 carrayClose, /* xClose - close a cursor */
230380 carrayFilter, /* xFilter - configure scan constraints */
230381 carrayNext, /* xNext - advance a cursor */
230382 carrayEof, /* xEof - check for end of scan */
230383 carrayColumn, /* xColumn - read data */
230384 carrayRowid, /* xRowid - read data */
230385 0, /* xUpdate */
230386 0, /* xBegin */
230387 0, /* xSync */
230388 0, /* xCommit */
230389 0, /* xRollback */
230390 0, /* xFindMethod */
230391 0, /* xRename */
230392 0, /* xSavepoint */
230393 0, /* xRelease */
230394 0, /* xRollbackTo */
230395 0, /* xShadow */
230396 0 /* xIntegrity */
230397 };
230398
230399 /*
230400 ** Destructor for the carray_bind object
230401 */
230402 static void carrayBindDel(void *pPtr){
230403 carray_bind *p = (carray_bind*)pPtr;
230404 if( p->xDel!=SQLITE_STATIC ){
230405 p->xDel(p->aData);
230406 }
230407 sqlite3_free(p);
230408 }
230409
230410 /*
230411 ** Invoke this interface in order to bind to the single-argument
230412 ** version of CARRAY().
230413 */
230414 SQLITE_API int sqlite3_carray_bind(
230415 sqlite3_stmt *pStmt,
230416 int idx,
230417 void *aData,
230418 int nData,
230419 int mFlags,
230420 void (*xDestroy)(void*)
230421 ){
230422 carray_bind *pNew = 0;
230423 int i;
230424 int rc = SQLITE_OK;
230425
230426 /* Ensure that the mFlags value is acceptable. */
230427 assert( CARRAY_INT32==0 && CARRAY_INT64==1 && CARRAY_DOUBLE==2 );
230428 assert( CARRAY_TEXT==3 && CARRAY_BLOB==4 );
230429 if( mFlags<CARRAY_INT32 || mFlags>CARRAY_BLOB ){
230430 rc = SQLITE_ERROR;
230431 goto carray_bind_error;
230432 }
230433
230434 pNew = sqlite3_malloc64(sizeof(*pNew));
230435 if( pNew==0 ){
230436 rc = SQLITE_NOMEM;
230437 goto carray_bind_error;
230438 }
230439
230440 pNew->nData = nData;
230441 pNew->mFlags = mFlags;
230442 if( xDestroy==SQLITE_TRANSIENT ){
230443 sqlite3_int64 sz = nData;
230444 switch( mFlags ){
230445 case CARRAY_INT32: sz *= 4; break;
230446 case CARRAY_INT64: sz *= 8; break;
230447 case CARRAY_DOUBLE: sz *= 8; break;
230448 case CARRAY_TEXT: sz *= sizeof(char*); break;
230449 default: sz *= sizeof(struct iovec); break;
230450 }
230451 if( mFlags==CARRAY_TEXT ){
230452 for(i=0; i<nData; i++){
230453 const char *z = ((char**)aData)[i];
230454 if( z ) sz += strlen(z) + 1;
230455 }
230456 }else if( mFlags==CARRAY_BLOB ){
230457 for(i=0; i<nData; i++){
230458 sz += ((struct iovec*)aData)[i].iov_len;
230459 }
230460 }
230461
230462 pNew->aData = sqlite3_malloc64( sz );
230463 if( pNew->aData==0 ){
230464 rc = SQLITE_NOMEM;
230465 goto carray_bind_error;
230466 }
230467
230468 if( mFlags==CARRAY_TEXT ){
230469 char **az = (char**)pNew->aData;
230470 char *z = (char*)&az[nData];
230471 for(i=0; i<nData; i++){
230472 const char *zData = ((char**)aData)[i];
230473 sqlite3_int64 n;
230474 if( zData==0 ){
230475 az[i] = 0;
230476 continue;
230477 }
230478 az[i] = z;
230479 n = strlen(zData);
230480 memcpy(z, zData, n+1);
230481 z += n+1;
230482 }
230483 }else if( mFlags==CARRAY_BLOB ){
230484 struct iovec *p = (struct iovec*)pNew->aData;
230485 unsigned char *z = (unsigned char*)&p[nData];
230486 for(i=0; i<nData; i++){
230487 size_t n = ((struct iovec*)aData)[i].iov_len;
230488 p[i].iov_len = n;
230489 p[i].iov_base = z;
230490 z += n;
230491 memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n);
230492 }
230493 }else{
230494 memcpy(pNew->aData, aData, sz);
230495 }
230496 pNew->xDel = sqlite3_free;
230497 }else{
230498 pNew->aData = aData;
230499 pNew->xDel = xDestroy;
230500 }
230501 return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
230502
230503 carray_bind_error:
230504 if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
230505 xDestroy(aData);
230506 }
230507 sqlite3_free(pNew);
230508 return rc;
230509 }
230510
230511 /*
230512 ** Invoke this routine to register the carray() function.
230513 */
230514 SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3 *db){
230515 return sqlite3VtabCreateModule(db, "carray", &carrayModule, 0, 0);
230516 }
230517
230518 #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY) */
230519
230520 /************** End of carray.c **********************************************/
230521 /************** Begin file sqlite3session.c **********************************/
230522
230523 #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
230524 /* #include "sqlite3session.h" */
230525 /* #include <assert.h> */
@@ -231561,10 +233333,23 @@
233333 assert( (a - p->aRecord)==p->nRecord );
233334 }
233335
233336 return rc;
233337 }
233338
233339 static int sessionPrepare(
233340 sqlite3 *db,
233341 sqlite3_stmt **pp,
233342 char **pzErrmsg,
233343 const char *zSql
233344 ){
233345 int rc = sqlite3_prepare_v2(db, zSql, -1, pp, 0);
233346 if( pzErrmsg && rc!=SQLITE_OK ){
233347 *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
233348 }
233349 return rc;
233350 }
233351
233352 /*
233353 ** Formulate and prepare a SELECT statement to retrieve a row from table
233354 ** zTab in database zDb based on its primary key. i.e.
233355 **
@@ -231583,16 +233368,16 @@
233368 const char *zTab, /* Table name */
233369 int bRowid,
233370 int nCol, /* Number of columns in table */
233371 const char **azCol, /* Names of table columns */
233372 u8 *abPK, /* PRIMARY KEY array */
233373 sqlite3_stmt **ppStmt, /* OUT: Prepared SELECT statement */
233374 char **pzErrmsg /* OUT: Error message */
233375 ){
233376 int rc = SQLITE_OK;
233377 char *zSql = 0;
233378 const char *zSep = "";
 
233379 int i;
233380
233381 SessionBuffer cols = {0, 0, 0};
233382 SessionBuffer nooptest = {0, 0, 0};
233383 SessionBuffer pkfield = {0, 0, 0};
@@ -231668,11 +233453,11 @@
233453 nSql = buf.nBuf;
233454 }
233455 #endif
233456
233457 if( rc==SQLITE_OK ){
233458 rc = sessionPrepare(db, ppStmt, pzErrmsg, zSql);
233459 }
233460 sqlite3_free(zSql);
233461 sqlite3_free(nooptest.aBuf);
233462 sqlite3_free(pkfield.aBuf);
233463 sqlite3_free(pkvar.aBuf);
@@ -231832,11 +233617,11 @@
233617 sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
233618
233619 /* Build and compile a statement to execute: */
233620 if( rc==SQLITE_OK ){
233621 rc = sessionSelectStmt(db, 0, pSession->zDb,
233622 zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel, 0
233623 );
233624 }
233625
233626 nNoop = buf.nBuf;
233627 for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
@@ -233041,10 +234826,11 @@
234826 SessionBuffer rebase; /* Rebase information (if any) here */
234827 u8 bRebaseStarted; /* If table header is already in rebase */
234828 u8 bRebase; /* True to collect rebase information */
234829 u8 bIgnoreNoop; /* True to ignore no-op conflicts */
234830 int bRowid;
234831 char *zErr; /* Error message, if any */
234832 };
234833
234834 /* Number of prepared UPDATE statements to cache. */
234835 #define SESSION_UPDATE_CACHE_SZ 12
234836
@@ -233266,11 +235052,11 @@
235052 }
235053 sessionAppendStr(&buf, ")", &rc);
235054 }
235055
235056 if( rc==SQLITE_OK ){
235057 rc = sessionPrepare(db, &p->pDelete, &p->zErr, (char*)buf.aBuf);
235058 }
235059 sqlite3_free(buf.aBuf);
235060
235061 return rc;
235062 }
@@ -233293,11 +235079,11 @@
235079 const char *zTab, /* Table name */
235080 SessionApplyCtx *p /* Session changeset-apply context */
235081 ){
235082 /* TODO */
235083 return sessionSelectStmt(db, p->bIgnoreNoop,
235084 "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect, &p->zErr
235085 );
235086 }
235087
235088 /*
235089 ** Formulate and prepare an INSERT statement to add a record to table zTab.
@@ -233330,37 +235116,33 @@
235116 sessionAppendStr(&buf, ", ?", &rc);
235117 }
235118 sessionAppendStr(&buf, ")", &rc);
235119
235120 if( rc==SQLITE_OK ){
235121 rc = sessionPrepare(db, &p->pInsert, &p->zErr, (char*)buf.aBuf);
235122 }
235123 sqlite3_free(buf.aBuf);
235124 return rc;
235125 }
235126
 
 
 
 
235127 /*
235128 ** Prepare statements for applying changes to the sqlite_stat1 table.
235129 ** These are similar to those created by sessionSelectRow(),
235130 ** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
235131 ** other tables.
235132 */
235133 static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
235134 int rc = sessionSelectRow(db, "sqlite_stat1", p);
235135 if( rc==SQLITE_OK ){
235136 rc = sessionPrepare(db, &p->pInsert, 0,
235137 "INSERT INTO main.sqlite_stat1 VALUES(?1, "
235138 "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, "
235139 "?3)"
235140 );
235141 }
235142 if( rc==SQLITE_OK ){
235143 rc = sessionPrepare(db, &p->pDelete, 0,
235144 "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
235145 "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
235146 "AND (?4 OR stat IS ?3)"
235147 );
235148 }
@@ -233580,11 +235362,11 @@
235362 sqlite3_changeset_iter *pIter, /* Changeset iterator */
235363 int(*xConflict)(void *, int, sqlite3_changeset_iter*),
235364 void *pCtx, /* First argument for conflict handler */
235365 int *pbReplace /* OUT: Set to true if PK row is found */
235366 ){
235367 int res = SQLITE_CHANGESET_OMIT;/* Value returned by conflict handler */
235368 int rc;
235369 int nCol;
235370 int op;
235371 const char *zDummy;
235372
@@ -233601,15 +235383,13 @@
235383 rc = SQLITE_OK;
235384 }
235385
235386 if( rc==SQLITE_ROW ){
235387 /* There exists another row with the new.* primary key. */
235388 if( 0==p->bIgnoreNoop
235389 || 0==sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
235390 ){
 
 
235391 pIter->pConflict = p->pSelect;
235392 res = xConflict(pCtx, eType, pIter);
235393 pIter->pConflict = 0;
235394 }
235395 rc = sqlite3_reset(p->pSelect);
@@ -233619,11 +235399,13 @@
235399 ** to the SessionApplyCtx.constraints buffer. */
235400 u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
235401 int nBlob = pIter->in.iNext - pIter->in.iCurrent;
235402 sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
235403 return SQLITE_OK;
235404 }else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE
235405 || eType==SQLITE_CHANGESET_CONFLICT
235406 ){
235407 /* No other row with the new.* primary key. */
235408 res = xConflict(pCtx, eType+1, pIter);
235409 if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
235410 }
235411 }
@@ -233717,11 +235499,11 @@
235499 }
235500 if( rc!=SQLITE_OK ) return rc;
235501
235502 sqlite3_step(p->pDelete);
235503 rc = sqlite3_reset(p->pDelete);
235504 if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
235505 rc = sessionConflictHandler(
235506 SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
235507 );
235508 }else if( (rc&0xff)==SQLITE_CONSTRAINT ){
235509 rc = sessionConflictHandler(
@@ -234122,10 +235904,11 @@
235904 }
235905 }
235906
235907 assert( sApply.bRebase || sApply.rebase.nBuf==0 );
235908 if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
235909 assert( ppRebase!=0 && pnRebase!=0 );
235910 *ppRebase = (void*)sApply.rebase.aBuf;
235911 *pnRebase = sApply.rebase.nBuf;
235912 sApply.rebase.aBuf = 0;
235913 }
235914 sessionUpdateFree(&sApply);
@@ -234139,10 +235922,15 @@
235922 if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
235923 assert( db->flags & SQLITE_FkNoAction );
235924 db->flags &= ~((u64)SQLITE_FkNoAction);
235925 db->aDb[0].pSchema->schema_cookie -= 32;
235926 }
235927
235928 assert( rc!=SQLITE_OK || sApply.zErr==0 );
235929 sqlite3_set_errmsg(db, rc, sApply.zErr);
235930 sqlite3_free(sApply.zErr);
235931
235932 sqlite3_mutex_leave(sqlite3_db_mutex(db));
235933 return rc;
235934 }
235935
235936 /*
@@ -236348,25 +238136,18 @@
238136 ** Constants for the largest and smallest possible 64-bit signed integers.
238137 */
238138 # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
238139 # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
238140
238141 /*
238142 ** This macro is used in a single assert() within fts5 to check that an
238143 ** allocation is aligned to an 8-byte boundary. But it is a complicated
238144 ** macro to get right for multiple platforms without generating warnings.
238145 ** So instead of reproducing the entire definition from sqliteInt.h, we
238146 ** just do without this assert() for the rare non-amalgamation builds.
238147 */
238148 #define EIGHT_BYTE_ALIGNMENT(x) 1
 
 
 
 
 
 
 
 
 
 
 
 
238149
238150 /*
238151 ** Macros needed to provide flexible arrays in a portable way
238152 */
238153 #ifndef offsetof
@@ -237110,11 +238891,11 @@
238891 ** ){
238892 ** // The document with rowid iRowid matches the expression!
238893 ** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
238894 ** }
238895 */
238896 static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, i64, int bDesc);
238897 static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax);
238898 static int sqlite3Fts5ExprEof(Fts5Expr*);
238899 static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
238900
238901 static void sqlite3Fts5ExprFree(Fts5Expr*);
@@ -242679,11 +244460,17 @@
244460 ** equal to iFirst.
244461 **
244462 ** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
244463 ** is not considered an error if the query does not match any documents.
244464 */
244465 static int sqlite3Fts5ExprFirst(
244466 Fts5Expr *p,
244467 Fts5Index *pIdx,
244468 i64 iFirst,
244469 i64 iLast,
244470 int bDesc
244471 ){
244472 Fts5ExprNode *pRoot = p->pRoot;
244473 int rc; /* Return code */
244474
244475 p->pIndex = pIdx;
244476 p->bDesc = bDesc;
@@ -242700,10 +244487,13 @@
244487
244488 /* If the iterator is not at a real match, skip forward until it is. */
244489 while( pRoot->bNomatch && rc==SQLITE_OK ){
244490 assert( pRoot->bEof==0 );
244491 rc = fts5ExprNodeNext(p, pRoot, 0, 0);
244492 }
244493 if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){
244494 pRoot->bEof = 1;
244495 }
244496 return rc;
244497 }
244498
244499 /*
@@ -245876,13 +247666,13 @@
247666 ** backing store corruption. */
247667 if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
247668
247669 if( rc==SQLITE_OK ){
247670 u8 *aOut = 0; /* Read blob data into this buffer */
247671 i64 nByte = sqlite3_blob_bytes(p->pReader);
247672 i64 szData = (sizeof(Fts5Data) + 7) & ~7;
247673 i64 nAlloc = szData + nByte + FTS5_DATA_PADDING;
247674 pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
247675 if( pRet ){
247676 pRet->nn = nByte;
247677 aOut = pRet->p = (u8*)pRet + szData;
247678 }else{
@@ -251821,15 +253611,18 @@
253611 ** function populates it with the initial structure objects for each index,
253612 ** and the initial version of the "averages" record (a zero-byte blob).
253613 */
253614 static int sqlite3Fts5IndexReinit(Fts5Index *p){
253615 Fts5Structure *pTmp;
253616 union {
253617 Fts5Structure sFts;
253618 u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
253619 } uFts;
253620 fts5StructureInvalidate(p);
253621 fts5IndexDiscardData(p);
253622 pTmp = &uFts.sFts;
253623 memset(uFts.tmpSpace, 0, sizeof(uFts.tmpSpace));
253624 if( p->pConfig->bContentlessDelete ){
253625 pTmp->nOriginCntr = 1;
253626 }
253627 fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
253628 fts5StructureWrite(p, pTmp);
@@ -255045,10 +256838,21 @@
256838 {
256839 pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
256840 }
256841 #endif
256842 }
256843
256844 static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
256845 #if SQLITE_VERSION_NUMBER>=3008002
256846 #ifndef SQLITE_CORE
256847 if( sqlite3_libversion_number()>=3008002 )
256848 #endif
256849 {
256850 pIdxInfo->estimatedRows = nRow;
256851 }
256852 #endif
256853 }
256854
256855 static int fts5UsePatternMatch(
256856 Fts5Config *pConfig,
256857 struct sqlite3_index_constraint *p
256858 ){
@@ -255181,11 +256985,11 @@
256985 bSeenRank = 1;
256986 }else{
256987 nSeenMatch++;
256988 idxStr[iIdxStr++] = 'M';
256989 sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
256990 iIdxStr += (int)strlen(&idxStr[iIdxStr]);
256991 assert( idxStr[iIdxStr]=='\0' );
256992 }
256993 pInfo->aConstraintUsage[i].argvIndex = ++iCons;
256994 pInfo->aConstraintUsage[i].omit = 1;
256995 }
@@ -255200,10 +257004,11 @@
257004 nSeenMatch++;
257005 }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
257006 idxStr[iIdxStr++] = '=';
257007 bSeenEq = 1;
257008 pInfo->aConstraintUsage[i].argvIndex = ++iCons;
257009 pInfo->aConstraintUsage[i].omit = 1;
257010 }
257011 }
257012 }
257013
257014 if( bSeenEq==0 ){
@@ -255247,21 +257052,25 @@
257052 }
257053 }
257054
257055 /* Calculate the estimated cost based on the flags set in idxFlags. */
257056 if( bSeenEq ){
257057 pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
257058 fts5SetUniqueFlag(pInfo);
257059 fts5SetEstimatedRows(pInfo, 1);
 
 
 
257060 }else{
257061 if( bSeenLt && bSeenGt ){
257062 pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0;
257063 }else if( bSeenLt || bSeenGt ){
257064 pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0;
257065 }else{
257066 pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
257067 }
257068 for(i=1; i<nSeenMatch; i++){
257069 pInfo->estimatedCost *= 0.4;
257070 }
257071 fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
257072 }
257073
257074 pInfo->idxNum = idxFlags;
257075 return SQLITE_OK;
257076 }
@@ -255456,11 +257265,13 @@
257265 if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
257266 Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
257267 int bDesc = pCsr->bDesc;
257268 i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
257269
257270 rc = sqlite3Fts5ExprFirst(
257271 pCsr->pExpr, pTab->p.pIndex, iRowid, pCsr->iLastRowid, bDesc
257272 );
257273 if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
257274 *pbSkip = 1;
257275 }
257276
257277 CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
@@ -255628,11 +257439,13 @@
257439 }
257440
257441 static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
257442 int rc;
257443 Fts5Expr *pExpr = pCsr->pExpr;
257444 rc = sqlite3Fts5ExprFirst(
257445 pExpr, pTab->p.pIndex, pCsr->iFirstRowid, pCsr->iLastRowid, bDesc
257446 );
257447 if( sqlite3Fts5ExprEof(pExpr) ){
257448 CsrFlagSet(pCsr, FTS5CSR_EOF);
257449 }
257450 fts5CsrNewrow(pCsr);
257451 return rc;
@@ -258113,11 +259926,11 @@
259926 int nArg, /* Number of args */
259927 sqlite3_value **apUnused /* Function arguments */
259928 ){
259929 assert( nArg==0 );
259930 UNUSED_PARAM2(nArg, apUnused);
259931 sqlite3_result_text(pCtx, "fts5: 2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70", -1, SQLITE_TRANSIENT);
259932 }
259933
259934 /*
259935 ** Implementation of fts5_locale(LOCALE, TEXT) function.
259936 **
@@ -258136,13 +259949,13 @@
259949 sqlite3_context *pCtx, /* Function call context */
259950 int nArg, /* Number of args */
259951 sqlite3_value **apArg /* Function arguments */
259952 ){
259953 const char *zLocale = 0;
259954 i64 nLocale = 0;
259955 const char *zText = 0;
259956 i64 nText = 0;
259957
259958 assert( nArg==2 );
259959 UNUSED_PARAM(nArg);
259960
259961 zLocale = (const char*)sqlite3_value_text(apArg[0]);
@@ -258155,14 +259968,14 @@
259968 sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
259969 }else{
259970 Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx);
259971 u8 *pBlob = 0;
259972 u8 *pCsr = 0;
259973 i64 nBlob = 0;
259974
259975 nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText;
259976 pBlob = (u8*)sqlite3_malloc64(nBlob);
259977 if( pBlob==0 ){
259978 sqlite3_result_error_nomem(pCtx);
259979 return;
259980 }
259981
259982
+178 -42
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,14 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.51.0"
150150
#define SQLITE_VERSION_NUMBER 3051000
151
-#define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
151
+#define SQLITE_SOURCE_ID "2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70"
152
+#define SQLITE_SCM_BRANCH "trunk"
153
+#define SQLITE_SCM_TAGS ""
154
+#define SQLITE_SCM_DATETIME "2025-10-15T10:52:45.276Z"
152155
153156
/*
154157
** CAPI3REF: Run-Time Library Version Numbers
155158
** KEYWORDS: sqlite3_version sqlite3_sourceid
156159
**
@@ -495,10 +498,13 @@
495498
** [sqlite3_extended_errcode()].
496499
*/
497500
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
498501
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
499502
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
503
+#define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
504
+#define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
505
+#define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
500506
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
501507
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
502508
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
503509
#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
504510
#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -529,10 +535,12 @@
529535
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
530536
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
531537
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
532538
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
533539
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
540
+#define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
541
+#define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
534542
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
535543
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
536544
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
537545
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
538546
#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -2333,21 +2341,24 @@
23332341
** views in the main database schema or in the schemas of ATTACH-ed
23342342
** databases.)^ </dd>
23352343
**
23362344
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
23372345
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2338
-** <dd> ^This option is used to enable or disable the
2339
-** [fts3_tokenizer()] function which is part of the
2340
-** [FTS3] full-text search engine extension.
2341
-** There must be two additional arguments.
2342
-** The first argument is an integer which is 0 to disable fts3_tokenizer() or
2343
-** positive to enable fts3_tokenizer() or negative to leave the setting
2344
-** unchanged.
2345
-** The second parameter is a pointer to an integer into which
2346
-** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
2347
-** following this call. The second parameter may be a NULL pointer, in
2348
-** which case the new setting is not reported back. </dd>
2346
+** <dd> ^This option is used to enable or disable using the
2347
+** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine
2348
+** extension - without using bound parameters as the parameters. Doing so
2349
+** is disabled by default. There must be two additional arguments. The first
2350
+** argument is an integer. If it is passed 0, then using fts3_tokenizer()
2351
+** without bound parameters is disabled. If it is passed a positive value,
2352
+** then calling fts3_tokenizer without bound parameters is enabled. If it
2353
+** is passed a negative value, this setting is not modified - this can be
2354
+** used to query for the current setting. The second parameter is a pointer
2355
+** to an integer into which is written 0 or 1 to indicate the current value
2356
+** of this setting (after it is modified, if applicable). The second
2357
+** parameter may be a NULL pointer, in which case the value of the setting
2358
+** is not reported back. Refer to [FTS3] documentation for further details.
2359
+** </dd>
23492360
**
23502361
** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
23512362
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
23522363
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
23532364
** interface independently of the [load_extension()] SQL function.
@@ -4193,10 +4204,38 @@
41934204
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
41944205
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
41954206
SQLITE_API const char *sqlite3_errstr(int);
41964207
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
41974208
4209
+/*
4210
+** CAPI3REF: Set Error Codes And Message
4211
+** METHOD: sqlite3
4212
+**
4213
+** Set the error code of the database handle passed as the first argument
4214
+** to errcode, and the error message to a copy of nul-terminated string
4215
+** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
4216
+** the default message associated with the supplied error code. Subsequent
4217
+** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
4218
+** return the values set by this routine in place of what was previously
4219
+** set by SQLite itself.
4220
+**
4221
+** This function returns SQLITE_OK if the error code and error message are
4222
+** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
4223
+** the database handle is NULL or invalid.
4224
+**
4225
+** The error code and message set by this routine remains in effect until
4226
+** they are changed, either by another call to this routine or until they are
4227
+** changed to by SQLite itself to reflect the result of some subsquent
4228
+** API call.
4229
+**
4230
+** This function is intended for use by SQLite extensions or wrappers. The
4231
+** idea is that an extension or wrapper can use this routine to set error
4232
+** messages and error codes and thus behave more like a core SQLite
4233
+** feature from the point of view of an application.
4234
+*/
4235
+SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
4236
+
41984237
/*
41994238
** CAPI3REF: Prepared Statement Object
42004239
** KEYWORDS: {prepared statement} {prepared statements}
42014240
**
42024241
** An instance of this object represents a single SQL statement that
@@ -6203,10 +6242,11 @@
62036242
** to be attached to [database connection] D using name N. Subsequent
62046243
** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
62056244
** or a NULL pointer if there were no prior calls to
62066245
** sqlite3_set_clientdata() with the same values of D and N.
62076246
** Names are compared using strcmp() and are thus case sensitive.
6247
+** It returns 0 on success and SQLITE_NOMEM on allocation failure.
62086248
**
62096249
** If P and X are both non-NULL, then the destructor X is invoked with
62106250
** argument P on the first of the following occurrences:
62116251
** <ul>
62126252
** <li> An out-of-memory error occurs during the call to
@@ -8878,14 +8918,23 @@
88788918
** the resetFlg is true, then the highest instantaneous value is
88798919
** reset back down to the current value.
88808920
**
88818921
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
88828922
** non-zero [error code] on failure.
8923
+**
8924
+** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
8925
+** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
8926
+** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
8927
+** of pointers to 32-bit integers, which allows larger status values
8928
+** to be returned. If a status value exceeds 2,147,483,647 then
8929
+** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
8930
+** will return the full value.
88838931
**
88848932
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
88858933
*/
88868934
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
8935
+SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
88878936
88888937
/*
88898938
** CAPI3REF: Status Parameters for database connections
88908939
** KEYWORDS: {SQLITE_DBSTATUS options}
88918940
**
@@ -8978,10 +9027,14 @@
89789027
** database file in rollback mode databases. Any pages written as part of
89799028
** transaction rollback or database recovery operations are not included.
89809029
** If an IO or other error occurs while writing a page to disk, the effect
89819030
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
89829031
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
9032
+** <p>
9033
+** ^(There is overlap between the quantities measured by this parameter
9034
+** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL.
9035
+** Resetting one will reduce the other.)^
89839036
** </dd>
89849037
**
89859038
** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
89869039
** <dd>This parameter returns the number of dirty cache entries that have
89879040
** been written to disk in the middle of a transaction due to the page
@@ -8993,10 +9046,22 @@
89939046
**
89949047
** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
89959048
** <dd>This parameter returns zero for the current value if and only if
89969049
** all foreign key constraints (deferred or immediate) have been
89979050
** resolved.)^ ^The highwater mark is always 0.
9051
+**
9052
+** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt>
9053
+** <dd>^(This parameter returns the number of bytes written to temporary
9054
+** files on disk that could have been kept in memory had sufficient memory
9055
+** been available. This value includes writes to intermediate tables that
9056
+** are part of complex queries, external sorts that spill to disk, and
9057
+** writes to TEMP tables.)^
9058
+** ^The highwater mark is always 0.
9059
+** <p>
9060
+** ^(There is overlap between the quantities measured by this parameter
9061
+** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE.
9062
+** Resetting one will reduce the other.)^
89989063
** </dd>
89999064
** </dl>
90009065
*/
90019066
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
90029067
#define SQLITE_DBSTATUS_CACHE_USED 1
@@ -9009,11 +9074,12 @@
90099074
#define SQLITE_DBSTATUS_CACHE_MISS 8
90109075
#define SQLITE_DBSTATUS_CACHE_WRITE 9
90119076
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
90129077
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
90139078
#define SQLITE_DBSTATUS_CACHE_SPILL 12
9014
-#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
9079
+#define SQLITE_DBSTATUS_TEMPBUF_SPILL 13
9080
+#define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */
90159081
90169082
90179083
/*
90189084
** CAPI3REF: Prepared Statement Status
90199085
** METHOD: sqlite3_stmt
@@ -9774,25 +9840,38 @@
97749840
** ^The third parameter is the name of the database that was written to -
97759841
** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
97769842
** is the number of pages currently in the write-ahead log file,
97779843
** including those that were just committed.
97789844
**
9779
-** The callback function should normally return [SQLITE_OK]. ^If an error
9845
+** ^The callback function should normally return [SQLITE_OK]. ^If an error
97809846
** code is returned, that error will propagate back up through the
97819847
** SQLite code base to cause the statement that provoked the callback
97829848
** to report an error, though the commit will have still occurred. If the
97839849
** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
97849850
** that does not correspond to any valid SQLite error code, the results
97859851
** are undefined.
97869852
**
9787
-** A single database handle may have at most a single write-ahead log callback
9788
-** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
9789
-** previously registered write-ahead log callback. ^The return value is
9790
-** a copy of the third parameter from the previous call, if any, or 0.
9791
-** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
9792
-** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
9793
-** overwrite any prior [sqlite3_wal_hook()] settings.
9853
+** ^A single database handle may have at most a single write-ahead log
9854
+** callback registered at one time. ^Calling [sqlite3_wal_hook()]
9855
+** replaces the default behavior or previously registered write-ahead
9856
+** log callback.
9857
+**
9858
+** ^The return value is a copy of the third parameter from the
9859
+** previous call, if any, or 0.
9860
+**
9861
+** ^The [sqlite3_wal_autocheckpoint()] interface and the
9862
+** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and
9863
+** will overwrite any prior [sqlite3_wal_hook()] settings.
9864
+**
9865
+** ^If a write-ahead log callback is set using this function then
9866
+** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint]
9867
+** should be invoked periodically to keep the write-ahead log file
9868
+** from growing without bound.
9869
+**
9870
+** ^Passing a NULL pointer for the callback disables automatic
9871
+** checkpointing entirely. To re-enable the default behavior, call
9872
+** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint].
97949873
*/
97959874
SQLITE_API void *sqlite3_wal_hook(
97969875
sqlite3*,
97979876
int(*)(void *,sqlite3*,const char*,int),
97989877
void*
@@ -9805,11 +9884,11 @@
98059884
** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
98069885
** [sqlite3_wal_hook()] that causes any database on [database connection] D
98079886
** to automatically [checkpoint]
98089887
** after committing a transaction if there are N or
98099888
** more frames in the [write-ahead log] file. ^Passing zero or
9810
-** a negative value as the nFrame parameter disables automatic
9889
+** a negative value as the N parameter disables automatic
98119890
** checkpoints entirely.
98129891
**
98139892
** ^The callback registered by this function replaces any existing callback
98149893
** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback
98159894
** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
@@ -9821,13 +9900,14 @@
98219900
** ^Checkpoints initiated by this mechanism are
98229901
** [sqlite3_wal_checkpoint_v2|PASSIVE].
98239902
**
98249903
** ^Every new [database connection] defaults to having the auto-checkpoint
98259904
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
9826
-** pages. The use of this interface
9827
-** is only necessary if the default setting is found to be suboptimal
9828
-** for a particular application.
9905
+** pages.
9906
+**
9907
+** ^The use of this interface is only necessary if the default setting
9908
+** is found to be suboptimal for a particular application.
98299909
*/
98309910
SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
98319911
98329912
/*
98339913
** CAPI3REF: Checkpoint a database
@@ -9888,10 +9968,15 @@
98889968
**
98899969
** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
98909970
** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
98919971
** addition that it also truncates the log file to zero bytes just prior
98929972
** to a successful return.
9973
+**
9974
+** <dt>SQLITE_CHECKPOINT_NOOP<dd>
9975
+** ^This mode always checkpoints zero frames. The only reason to invoke
9976
+** a NOOP checkpoint is to access the values returned by
9977
+** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt.
98939978
** </dl>
98949979
**
98959980
** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
98969981
** the log file or to -1 if the checkpoint could not run because
98979982
** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
@@ -9958,10 +10043,11 @@
995810043
** These constants define all valid values for the "checkpoint mode" passed
995910044
** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
996010045
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
996110046
** meaning of each of these checkpoint modes.
996210047
*/
10048
+#define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
996310049
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
996410050
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
996510051
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
996610052
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
996710053
@@ -10785,11 +10871,11 @@
1078510871
** to avoid a memory leak.
1078610872
**
1078710873
** The [sqlite3_snapshot_get()] interface is only available when the
1078810874
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
1078910875
*/
10790
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
10876
+SQLITE_API int sqlite3_snapshot_get(
1079110877
sqlite3 *db,
1079210878
const char *zSchema,
1079310879
sqlite3_snapshot **ppSnapshot
1079410880
);
1079510881
@@ -10834,11 +10920,11 @@
1083410920
** database connection in order to make it ready to use snapshots.)
1083510921
**
1083610922
** The [sqlite3_snapshot_open()] interface is only available when the
1083710923
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
1083810924
*/
10839
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
10925
+SQLITE_API int sqlite3_snapshot_open(
1084010926
sqlite3 *db,
1084110927
const char *zSchema,
1084210928
sqlite3_snapshot *pSnapshot
1084310929
);
1084410930
@@ -10851,11 +10937,11 @@
1085110937
** using this routine to avoid a memory leak.
1085210938
**
1085310939
** The [sqlite3_snapshot_free()] interface is only available when the
1085410940
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
1085510941
*/
10856
-SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
10942
+SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*);
1085710943
1085810944
/*
1085910945
** CAPI3REF: Compare the ages of two snapshot handles.
1086010946
** METHOD: sqlite3_snapshot
1086110947
**
@@ -10878,11 +10964,11 @@
1087810964
** snapshot, and a positive value if P1 is a newer snapshot than P2.
1087910965
**
1088010966
** This interface is only available if SQLite is compiled with the
1088110967
** [SQLITE_ENABLE_SNAPSHOT] option.
1088210968
*/
10883
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
10969
+SQLITE_API int sqlite3_snapshot_cmp(
1088410970
sqlite3_snapshot *p1,
1088510971
sqlite3_snapshot *p2
1088610972
);
1088710973
1088810974
/*
@@ -10906,11 +10992,11 @@
1090610992
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
1090710993
**
1090810994
** This interface is only available if SQLite is compiled with the
1090910995
** [SQLITE_ENABLE_SNAPSHOT] option.
1091010996
*/
10911
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
10997
+SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
1091210998
1091310999
/*
1091411000
** CAPI3REF: Serialize a database
1091511001
**
1091611002
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
@@ -10980,16 +11066,17 @@
1098011066
/*
1098111067
** CAPI3REF: Deserialize a database
1098211068
**
1098311069
** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
1098411070
** [database connection] D to disconnect from database S and then
10985
-** reopen S as an in-memory database based on the serialization contained
10986
-** in P. The serialized database P is N bytes in size. M is the size of
10987
-** the buffer P, which might be larger than N. If M is larger than N, and
10988
-** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
10989
-** permitted to add content to the in-memory database as long as the total
10990
-** size does not exceed M bytes.
11071
+** reopen S as an in-memory database based on the serialization
11072
+** contained in P. If S is a NULL pointer, the main database is
11073
+** used. The serialized database P is N bytes in size. M is the size
11074
+** of the buffer P, which might be larger than N. If M is larger than
11075
+** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then
11076
+** SQLite is permitted to add content to the in-memory database as
11077
+** long as the total size does not exceed M bytes.
1099111078
**
1099211079
** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
1099311080
** invoke sqlite3_free() on the serialization buffer when the database
1099411081
** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
1099511082
** SQLite will try to increase the buffer size using sqlite3_realloc64()
@@ -11052,10 +11139,56 @@
1105211139
*/
1105311140
#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
1105411141
#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
1105511142
#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
1105611143
11144
+/*
11145
+** CAPI3REF: Bind array values to the CARRAY table-valued function
11146
+**
11147
+** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to
11148
+** one of the first argument of the [carray() table-valued function]. The
11149
+** S parameter is a pointer to the [prepared statement] that uses the carray()
11150
+** functions. I is the parameter index to be bound. P is a pointer to the
11151
+** array to be bound, and N is the number of eements in the array. The
11152
+** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
11153
+** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to
11154
+** indicate the datatype of the array being bound. The X argument is not a
11155
+** NULL pointer, then SQLite will invoke the function X on the P parameter
11156
+** after it has finished using P.
11157
+*/
11158
+SQLITE_API SQLITE_API int sqlite3_carray_bind(
11159
+ sqlite3_stmt *pStmt, /* Statement to be bound */
11160
+ int i, /* Parameter index */
11161
+ void *aData, /* Pointer to array data */
11162
+ int nData, /* Number of data elements */
11163
+ int mFlags, /* CARRAY flags */
11164
+ void (*xDel)(void*) /* Destructor for aData */
11165
+);
11166
+
11167
+/*
11168
+** CAPI3REF: Datatypes for the CARRAY table-valued funtion
11169
+**
11170
+** The fifth argument to the [sqlite3_carray_bind()] interface musts be
11171
+** one of the following constants, to specify the datatype of the array
11172
+** that is being bound into the [carray table-valued function].
11173
+*/
11174
+#define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */
11175
+#define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */
11176
+#define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */
11177
+#define SQLITE_CARRAY_TEXT 3 /* Data is char* */
11178
+#define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */
11179
+
11180
+/*
11181
+** Versions of the above #defines that omit the initial SQLITE_, for
11182
+** legacy compatibility.
11183
+*/
11184
+#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
11185
+#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
11186
+#define CARRAY_DOUBLE 2 /* Data is doubles */
11187
+#define CARRAY_TEXT 3 /* Data is char* */
11188
+#define CARRAY_BLOB 4 /* Data is struct iovec */
11189
+
1105711190
/*
1105811191
** Undo the hack that converts floating point types to integer for
1105911192
** builds on processors without floating point support.
1106011193
*/
1106111194
#ifdef SQLITE_OMIT_FLOATING_POINT
@@ -12310,10 +12443,19 @@
1231012443
** CAPI3REF: Apply A Changeset To A Database
1231112444
**
1231212445
** Apply a changeset or patchset to a database. These functions attempt to
1231312446
** update the "main" database attached to handle db with the changes found in
1231412447
** the changeset passed via the second and third arguments.
12448
+**
12449
+** All changes made by these functions are enclosed in a savepoint transaction.
12450
+** If any other error (aside from a constraint failure when attempting to
12451
+** write to the target database) occurs, then the savepoint transaction is
12452
+** rolled back, restoring the target database to its original state, and an
12453
+** SQLite error code returned. Additionally, starting with version 3.51.0,
12454
+** an error code and error message that may be accessed using the
12455
+** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database
12456
+** handle.
1231512457
**
1231612458
** The fourth argument (xFilter) passed to these functions is the "filter
1231712459
** callback". This may be passed NULL, in which case all changes in the
1231812460
** changeset are applied to the database. For sqlite3changeset_apply() and
1231912461
** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
@@ -12448,16 +12590,10 @@
1244812590
** It is safe to execute SQL statements, including those that write to the
1244912591
** table that the callback related to, from within the xConflict callback.
1245012592
** This can be used to further customize the application's conflict
1245112593
** resolution strategy.
1245212594
**
12453
-** All changes made by these functions are enclosed in a savepoint transaction.
12454
-** If any other error (aside from a constraint failure when attempting to
12455
-** write to the target database) occurs, then the savepoint transaction is
12456
-** rolled back, restoring the target database to its original state, and an
12457
-** SQLite error code returned.
12458
-**
1245912595
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
1246012596
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
1246112597
** may set (*ppRebase) to point to a "rebase" that may be used with the
1246212598
** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
1246312599
** is set to the size of the buffer in bytes. It is the responsibility of the
1246412600
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,14 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.51.0"
150 #define SQLITE_VERSION_NUMBER 3051000
151 #define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
 
 
 
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -495,10 +498,13 @@
495 ** [sqlite3_extended_errcode()].
496 */
497 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
498 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
499 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
 
 
 
500 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
501 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
502 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
503 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
504 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -529,10 +535,12 @@
529 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
530 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
531 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
532 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
533 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
 
 
534 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
535 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
536 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
537 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
538 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -2333,21 +2341,24 @@
2333 ** views in the main database schema or in the schemas of ATTACH-ed
2334 ** databases.)^ </dd>
2335 **
2336 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
2337 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2338 ** <dd> ^This option is used to enable or disable the
2339 ** [fts3_tokenizer()] function which is part of the
2340 ** [FTS3] full-text search engine extension.
2341 ** There must be two additional arguments.
2342 ** The first argument is an integer which is 0 to disable fts3_tokenizer() or
2343 ** positive to enable fts3_tokenizer() or negative to leave the setting
2344 ** unchanged.
2345 ** The second parameter is a pointer to an integer into which
2346 ** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
2347 ** following this call. The second parameter may be a NULL pointer, in
2348 ** which case the new setting is not reported back. </dd>
 
 
 
2349 **
2350 ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
2351 ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
2352 ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
2353 ** interface independently of the [load_extension()] SQL function.
@@ -4193,10 +4204,38 @@
4193 SQLITE_API const char *sqlite3_errmsg(sqlite3*);
4194 SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
4195 SQLITE_API const char *sqlite3_errstr(int);
4196 SQLITE_API int sqlite3_error_offset(sqlite3 *db);
4197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4198 /*
4199 ** CAPI3REF: Prepared Statement Object
4200 ** KEYWORDS: {prepared statement} {prepared statements}
4201 **
4202 ** An instance of this object represents a single SQL statement that
@@ -6203,10 +6242,11 @@
6203 ** to be attached to [database connection] D using name N. Subsequent
6204 ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
6205 ** or a NULL pointer if there were no prior calls to
6206 ** sqlite3_set_clientdata() with the same values of D and N.
6207 ** Names are compared using strcmp() and are thus case sensitive.
 
6208 **
6209 ** If P and X are both non-NULL, then the destructor X is invoked with
6210 ** argument P on the first of the following occurrences:
6211 ** <ul>
6212 ** <li> An out-of-memory error occurs during the call to
@@ -8878,14 +8918,23 @@
8878 ** the resetFlg is true, then the highest instantaneous value is
8879 ** reset back down to the current value.
8880 **
8881 ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
8882 ** non-zero [error code] on failure.
 
 
 
 
 
 
 
 
8883 **
8884 ** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
8885 */
8886 SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
 
8887
8888 /*
8889 ** CAPI3REF: Status Parameters for database connections
8890 ** KEYWORDS: {SQLITE_DBSTATUS options}
8891 **
@@ -8978,10 +9027,14 @@
8978 ** database file in rollback mode databases. Any pages written as part of
8979 ** transaction rollback or database recovery operations are not included.
8980 ** If an IO or other error occurs while writing a page to disk, the effect
8981 ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
8982 ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
 
 
 
 
8983 ** </dd>
8984 **
8985 ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
8986 ** <dd>This parameter returns the number of dirty cache entries that have
8987 ** been written to disk in the middle of a transaction due to the page
@@ -8993,10 +9046,22 @@
8993 **
8994 ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
8995 ** <dd>This parameter returns zero for the current value if and only if
8996 ** all foreign key constraints (deferred or immediate) have been
8997 ** resolved.)^ ^The highwater mark is always 0.
 
 
 
 
 
 
 
 
 
 
 
 
8998 ** </dd>
8999 ** </dl>
9000 */
9001 #define SQLITE_DBSTATUS_LOOKASIDE_USED 0
9002 #define SQLITE_DBSTATUS_CACHE_USED 1
@@ -9009,11 +9074,12 @@
9009 #define SQLITE_DBSTATUS_CACHE_MISS 8
9010 #define SQLITE_DBSTATUS_CACHE_WRITE 9
9011 #define SQLITE_DBSTATUS_DEFERRED_FKS 10
9012 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
9013 #define SQLITE_DBSTATUS_CACHE_SPILL 12
9014 #define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
 
9015
9016
9017 /*
9018 ** CAPI3REF: Prepared Statement Status
9019 ** METHOD: sqlite3_stmt
@@ -9774,25 +9840,38 @@
9774 ** ^The third parameter is the name of the database that was written to -
9775 ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
9776 ** is the number of pages currently in the write-ahead log file,
9777 ** including those that were just committed.
9778 **
9779 ** The callback function should normally return [SQLITE_OK]. ^If an error
9780 ** code is returned, that error will propagate back up through the
9781 ** SQLite code base to cause the statement that provoked the callback
9782 ** to report an error, though the commit will have still occurred. If the
9783 ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
9784 ** that does not correspond to any valid SQLite error code, the results
9785 ** are undefined.
9786 **
9787 ** A single database handle may have at most a single write-ahead log callback
9788 ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
9789 ** previously registered write-ahead log callback. ^The return value is
9790 ** a copy of the third parameter from the previous call, if any, or 0.
9791 ** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
9792 ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
9793 ** overwrite any prior [sqlite3_wal_hook()] settings.
 
 
 
 
 
 
 
 
 
 
 
 
 
9794 */
9795 SQLITE_API void *sqlite3_wal_hook(
9796 sqlite3*,
9797 int(*)(void *,sqlite3*,const char*,int),
9798 void*
@@ -9805,11 +9884,11 @@
9805 ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
9806 ** [sqlite3_wal_hook()] that causes any database on [database connection] D
9807 ** to automatically [checkpoint]
9808 ** after committing a transaction if there are N or
9809 ** more frames in the [write-ahead log] file. ^Passing zero or
9810 ** a negative value as the nFrame parameter disables automatic
9811 ** checkpoints entirely.
9812 **
9813 ** ^The callback registered by this function replaces any existing callback
9814 ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback
9815 ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
@@ -9821,13 +9900,14 @@
9821 ** ^Checkpoints initiated by this mechanism are
9822 ** [sqlite3_wal_checkpoint_v2|PASSIVE].
9823 **
9824 ** ^Every new [database connection] defaults to having the auto-checkpoint
9825 ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
9826 ** pages. The use of this interface
9827 ** is only necessary if the default setting is found to be suboptimal
9828 ** for a particular application.
 
9829 */
9830 SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
9831
9832 /*
9833 ** CAPI3REF: Checkpoint a database
@@ -9888,10 +9968,15 @@
9888 **
9889 ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
9890 ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
9891 ** addition that it also truncates the log file to zero bytes just prior
9892 ** to a successful return.
 
 
 
 
 
9893 ** </dl>
9894 **
9895 ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
9896 ** the log file or to -1 if the checkpoint could not run because
9897 ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
@@ -9958,10 +10043,11 @@
9958 ** These constants define all valid values for the "checkpoint mode" passed
9959 ** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
9960 ** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
9961 ** meaning of each of these checkpoint modes.
9962 */
 
9963 #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
9964 #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
9965 #define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
9966 #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
9967
@@ -10785,11 +10871,11 @@
10785 ** to avoid a memory leak.
10786 **
10787 ** The [sqlite3_snapshot_get()] interface is only available when the
10788 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
10789 */
10790 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
10791 sqlite3 *db,
10792 const char *zSchema,
10793 sqlite3_snapshot **ppSnapshot
10794 );
10795
@@ -10834,11 +10920,11 @@
10834 ** database connection in order to make it ready to use snapshots.)
10835 **
10836 ** The [sqlite3_snapshot_open()] interface is only available when the
10837 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
10838 */
10839 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
10840 sqlite3 *db,
10841 const char *zSchema,
10842 sqlite3_snapshot *pSnapshot
10843 );
10844
@@ -10851,11 +10937,11 @@
10851 ** using this routine to avoid a memory leak.
10852 **
10853 ** The [sqlite3_snapshot_free()] interface is only available when the
10854 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
10855 */
10856 SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
10857
10858 /*
10859 ** CAPI3REF: Compare the ages of two snapshot handles.
10860 ** METHOD: sqlite3_snapshot
10861 **
@@ -10878,11 +10964,11 @@
10878 ** snapshot, and a positive value if P1 is a newer snapshot than P2.
10879 **
10880 ** This interface is only available if SQLite is compiled with the
10881 ** [SQLITE_ENABLE_SNAPSHOT] option.
10882 */
10883 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
10884 sqlite3_snapshot *p1,
10885 sqlite3_snapshot *p2
10886 );
10887
10888 /*
@@ -10906,11 +10992,11 @@
10906 ** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
10907 **
10908 ** This interface is only available if SQLite is compiled with the
10909 ** [SQLITE_ENABLE_SNAPSHOT] option.
10910 */
10911 SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
10912
10913 /*
10914 ** CAPI3REF: Serialize a database
10915 **
10916 ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
@@ -10980,16 +11066,17 @@
10980 /*
10981 ** CAPI3REF: Deserialize a database
10982 **
10983 ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
10984 ** [database connection] D to disconnect from database S and then
10985 ** reopen S as an in-memory database based on the serialization contained
10986 ** in P. The serialized database P is N bytes in size. M is the size of
10987 ** the buffer P, which might be larger than N. If M is larger than N, and
10988 ** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
10989 ** permitted to add content to the in-memory database as long as the total
10990 ** size does not exceed M bytes.
 
10991 **
10992 ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
10993 ** invoke sqlite3_free() on the serialization buffer when the database
10994 ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
10995 ** SQLite will try to increase the buffer size using sqlite3_realloc64()
@@ -11052,10 +11139,56 @@
11052 */
11053 #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
11054 #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
11055 #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
11056
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11057 /*
11058 ** Undo the hack that converts floating point types to integer for
11059 ** builds on processors without floating point support.
11060 */
11061 #ifdef SQLITE_OMIT_FLOATING_POINT
@@ -12310,10 +12443,19 @@
12310 ** CAPI3REF: Apply A Changeset To A Database
12311 **
12312 ** Apply a changeset or patchset to a database. These functions attempt to
12313 ** update the "main" database attached to handle db with the changes found in
12314 ** the changeset passed via the second and third arguments.
 
 
 
 
 
 
 
 
 
12315 **
12316 ** The fourth argument (xFilter) passed to these functions is the "filter
12317 ** callback". This may be passed NULL, in which case all changes in the
12318 ** changeset are applied to the database. For sqlite3changeset_apply() and
12319 ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
@@ -12448,16 +12590,10 @@
12448 ** It is safe to execute SQL statements, including those that write to the
12449 ** table that the callback related to, from within the xConflict callback.
12450 ** This can be used to further customize the application's conflict
12451 ** resolution strategy.
12452 **
12453 ** All changes made by these functions are enclosed in a savepoint transaction.
12454 ** If any other error (aside from a constraint failure when attempting to
12455 ** write to the target database) occurs, then the savepoint transaction is
12456 ** rolled back, restoring the target database to its original state, and an
12457 ** SQLite error code returned.
12458 **
12459 ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
12460 ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
12461 ** may set (*ppRebase) to point to a "rebase" that may be used with the
12462 ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
12463 ** is set to the size of the buffer in bytes. It is the responsibility of the
12464
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,14 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.51.0"
150 #define SQLITE_VERSION_NUMBER 3051000
151 #define SQLITE_SOURCE_ID "2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70"
152 #define SQLITE_SCM_BRANCH "trunk"
153 #define SQLITE_SCM_TAGS ""
154 #define SQLITE_SCM_DATETIME "2025-10-15T10:52:45.276Z"
155
156 /*
157 ** CAPI3REF: Run-Time Library Version Numbers
158 ** KEYWORDS: sqlite3_version sqlite3_sourceid
159 **
@@ -495,10 +498,13 @@
498 ** [sqlite3_extended_errcode()].
499 */
500 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
501 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
502 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
503 #define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
504 #define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
505 #define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
506 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
507 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
508 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
509 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
510 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -529,10 +535,12 @@
535 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
536 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
537 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
538 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
539 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
540 #define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
541 #define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
542 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
543 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
544 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
545 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
546 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -2333,21 +2341,24 @@
2341 ** views in the main database schema or in the schemas of ATTACH-ed
2342 ** databases.)^ </dd>
2343 **
2344 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
2345 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2346 ** <dd> ^This option is used to enable or disable using the
2347 ** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine
2348 ** extension - without using bound parameters as the parameters. Doing so
2349 ** is disabled by default. There must be two additional arguments. The first
2350 ** argument is an integer. If it is passed 0, then using fts3_tokenizer()
2351 ** without bound parameters is disabled. If it is passed a positive value,
2352 ** then calling fts3_tokenizer without bound parameters is enabled. If it
2353 ** is passed a negative value, this setting is not modified - this can be
2354 ** used to query for the current setting. The second parameter is a pointer
2355 ** to an integer into which is written 0 or 1 to indicate the current value
2356 ** of this setting (after it is modified, if applicable). The second
2357 ** parameter may be a NULL pointer, in which case the value of the setting
2358 ** is not reported back. Refer to [FTS3] documentation for further details.
2359 ** </dd>
2360 **
2361 ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
2362 ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
2363 ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
2364 ** interface independently of the [load_extension()] SQL function.
@@ -4193,10 +4204,38 @@
4204 SQLITE_API const char *sqlite3_errmsg(sqlite3*);
4205 SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
4206 SQLITE_API const char *sqlite3_errstr(int);
4207 SQLITE_API int sqlite3_error_offset(sqlite3 *db);
4208
4209 /*
4210 ** CAPI3REF: Set Error Codes And Message
4211 ** METHOD: sqlite3
4212 **
4213 ** Set the error code of the database handle passed as the first argument
4214 ** to errcode, and the error message to a copy of nul-terminated string
4215 ** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
4216 ** the default message associated with the supplied error code. Subsequent
4217 ** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
4218 ** return the values set by this routine in place of what was previously
4219 ** set by SQLite itself.
4220 **
4221 ** This function returns SQLITE_OK if the error code and error message are
4222 ** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
4223 ** the database handle is NULL or invalid.
4224 **
4225 ** The error code and message set by this routine remains in effect until
4226 ** they are changed, either by another call to this routine or until they are
4227 ** changed to by SQLite itself to reflect the result of some subsquent
4228 ** API call.
4229 **
4230 ** This function is intended for use by SQLite extensions or wrappers. The
4231 ** idea is that an extension or wrapper can use this routine to set error
4232 ** messages and error codes and thus behave more like a core SQLite
4233 ** feature from the point of view of an application.
4234 */
4235 SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
4236
4237 /*
4238 ** CAPI3REF: Prepared Statement Object
4239 ** KEYWORDS: {prepared statement} {prepared statements}
4240 **
4241 ** An instance of this object represents a single SQL statement that
@@ -6203,10 +6242,11 @@
6242 ** to be attached to [database connection] D using name N. Subsequent
6243 ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
6244 ** or a NULL pointer if there were no prior calls to
6245 ** sqlite3_set_clientdata() with the same values of D and N.
6246 ** Names are compared using strcmp() and are thus case sensitive.
6247 ** It returns 0 on success and SQLITE_NOMEM on allocation failure.
6248 **
6249 ** If P and X are both non-NULL, then the destructor X is invoked with
6250 ** argument P on the first of the following occurrences:
6251 ** <ul>
6252 ** <li> An out-of-memory error occurs during the call to
@@ -8878,14 +8918,23 @@
8918 ** the resetFlg is true, then the highest instantaneous value is
8919 ** reset back down to the current value.
8920 **
8921 ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
8922 ** non-zero [error code] on failure.
8923 **
8924 ** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
8925 ** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
8926 ** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
8927 ** of pointers to 32-bit integers, which allows larger status values
8928 ** to be returned. If a status value exceeds 2,147,483,647 then
8929 ** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
8930 ** will return the full value.
8931 **
8932 ** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
8933 */
8934 SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
8935 SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
8936
8937 /*
8938 ** CAPI3REF: Status Parameters for database connections
8939 ** KEYWORDS: {SQLITE_DBSTATUS options}
8940 **
@@ -8978,10 +9027,14 @@
9027 ** database file in rollback mode databases. Any pages written as part of
9028 ** transaction rollback or database recovery operations are not included.
9029 ** If an IO or other error occurs while writing a page to disk, the effect
9030 ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
9031 ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
9032 ** <p>
9033 ** ^(There is overlap between the quantities measured by this parameter
9034 ** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL.
9035 ** Resetting one will reduce the other.)^
9036 ** </dd>
9037 **
9038 ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
9039 ** <dd>This parameter returns the number of dirty cache entries that have
9040 ** been written to disk in the middle of a transaction due to the page
@@ -8993,10 +9046,22 @@
9046 **
9047 ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
9048 ** <dd>This parameter returns zero for the current value if and only if
9049 ** all foreign key constraints (deferred or immediate) have been
9050 ** resolved.)^ ^The highwater mark is always 0.
9051 **
9052 ** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt>
9053 ** <dd>^(This parameter returns the number of bytes written to temporary
9054 ** files on disk that could have been kept in memory had sufficient memory
9055 ** been available. This value includes writes to intermediate tables that
9056 ** are part of complex queries, external sorts that spill to disk, and
9057 ** writes to TEMP tables.)^
9058 ** ^The highwater mark is always 0.
9059 ** <p>
9060 ** ^(There is overlap between the quantities measured by this parameter
9061 ** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE.
9062 ** Resetting one will reduce the other.)^
9063 ** </dd>
9064 ** </dl>
9065 */
9066 #define SQLITE_DBSTATUS_LOOKASIDE_USED 0
9067 #define SQLITE_DBSTATUS_CACHE_USED 1
@@ -9009,11 +9074,12 @@
9074 #define SQLITE_DBSTATUS_CACHE_MISS 8
9075 #define SQLITE_DBSTATUS_CACHE_WRITE 9
9076 #define SQLITE_DBSTATUS_DEFERRED_FKS 10
9077 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
9078 #define SQLITE_DBSTATUS_CACHE_SPILL 12
9079 #define SQLITE_DBSTATUS_TEMPBUF_SPILL 13
9080 #define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */
9081
9082
9083 /*
9084 ** CAPI3REF: Prepared Statement Status
9085 ** METHOD: sqlite3_stmt
@@ -9774,25 +9840,38 @@
9840 ** ^The third parameter is the name of the database that was written to -
9841 ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
9842 ** is the number of pages currently in the write-ahead log file,
9843 ** including those that were just committed.
9844 **
9845 ** ^The callback function should normally return [SQLITE_OK]. ^If an error
9846 ** code is returned, that error will propagate back up through the
9847 ** SQLite code base to cause the statement that provoked the callback
9848 ** to report an error, though the commit will have still occurred. If the
9849 ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
9850 ** that does not correspond to any valid SQLite error code, the results
9851 ** are undefined.
9852 **
9853 ** ^A single database handle may have at most a single write-ahead log
9854 ** callback registered at one time. ^Calling [sqlite3_wal_hook()]
9855 ** replaces the default behavior or previously registered write-ahead
9856 ** log callback.
9857 **
9858 ** ^The return value is a copy of the third parameter from the
9859 ** previous call, if any, or 0.
9860 **
9861 ** ^The [sqlite3_wal_autocheckpoint()] interface and the
9862 ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and
9863 ** will overwrite any prior [sqlite3_wal_hook()] settings.
9864 **
9865 ** ^If a write-ahead log callback is set using this function then
9866 ** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint]
9867 ** should be invoked periodically to keep the write-ahead log file
9868 ** from growing without bound.
9869 **
9870 ** ^Passing a NULL pointer for the callback disables automatic
9871 ** checkpointing entirely. To re-enable the default behavior, call
9872 ** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint].
9873 */
9874 SQLITE_API void *sqlite3_wal_hook(
9875 sqlite3*,
9876 int(*)(void *,sqlite3*,const char*,int),
9877 void*
@@ -9805,11 +9884,11 @@
9884 ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
9885 ** [sqlite3_wal_hook()] that causes any database on [database connection] D
9886 ** to automatically [checkpoint]
9887 ** after committing a transaction if there are N or
9888 ** more frames in the [write-ahead log] file. ^Passing zero or
9889 ** a negative value as the N parameter disables automatic
9890 ** checkpoints entirely.
9891 **
9892 ** ^The callback registered by this function replaces any existing callback
9893 ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback
9894 ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
@@ -9821,13 +9900,14 @@
9900 ** ^Checkpoints initiated by this mechanism are
9901 ** [sqlite3_wal_checkpoint_v2|PASSIVE].
9902 **
9903 ** ^Every new [database connection] defaults to having the auto-checkpoint
9904 ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
9905 ** pages.
9906 **
9907 ** ^The use of this interface is only necessary if the default setting
9908 ** is found to be suboptimal for a particular application.
9909 */
9910 SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
9911
9912 /*
9913 ** CAPI3REF: Checkpoint a database
@@ -9888,10 +9968,15 @@
9968 **
9969 ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
9970 ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
9971 ** addition that it also truncates the log file to zero bytes just prior
9972 ** to a successful return.
9973 **
9974 ** <dt>SQLITE_CHECKPOINT_NOOP<dd>
9975 ** ^This mode always checkpoints zero frames. The only reason to invoke
9976 ** a NOOP checkpoint is to access the values returned by
9977 ** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt.
9978 ** </dl>
9979 **
9980 ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
9981 ** the log file or to -1 if the checkpoint could not run because
9982 ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
@@ -9958,10 +10043,11 @@
10043 ** These constants define all valid values for the "checkpoint mode" passed
10044 ** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
10045 ** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
10046 ** meaning of each of these checkpoint modes.
10047 */
10048 #define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
10049 #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
10050 #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
10051 #define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
10052 #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
10053
@@ -10785,11 +10871,11 @@
10871 ** to avoid a memory leak.
10872 **
10873 ** The [sqlite3_snapshot_get()] interface is only available when the
10874 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
10875 */
10876 SQLITE_API int sqlite3_snapshot_get(
10877 sqlite3 *db,
10878 const char *zSchema,
10879 sqlite3_snapshot **ppSnapshot
10880 );
10881
@@ -10834,11 +10920,11 @@
10920 ** database connection in order to make it ready to use snapshots.)
10921 **
10922 ** The [sqlite3_snapshot_open()] interface is only available when the
10923 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
10924 */
10925 SQLITE_API int sqlite3_snapshot_open(
10926 sqlite3 *db,
10927 const char *zSchema,
10928 sqlite3_snapshot *pSnapshot
10929 );
10930
@@ -10851,11 +10937,11 @@
10937 ** using this routine to avoid a memory leak.
10938 **
10939 ** The [sqlite3_snapshot_free()] interface is only available when the
10940 ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
10941 */
10942 SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*);
10943
10944 /*
10945 ** CAPI3REF: Compare the ages of two snapshot handles.
10946 ** METHOD: sqlite3_snapshot
10947 **
@@ -10878,11 +10964,11 @@
10964 ** snapshot, and a positive value if P1 is a newer snapshot than P2.
10965 **
10966 ** This interface is only available if SQLite is compiled with the
10967 ** [SQLITE_ENABLE_SNAPSHOT] option.
10968 */
10969 SQLITE_API int sqlite3_snapshot_cmp(
10970 sqlite3_snapshot *p1,
10971 sqlite3_snapshot *p2
10972 );
10973
10974 /*
@@ -10906,11 +10992,11 @@
10992 ** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
10993 **
10994 ** This interface is only available if SQLite is compiled with the
10995 ** [SQLITE_ENABLE_SNAPSHOT] option.
10996 */
10997 SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
10998
10999 /*
11000 ** CAPI3REF: Serialize a database
11001 **
11002 ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
@@ -10980,16 +11066,17 @@
11066 /*
11067 ** CAPI3REF: Deserialize a database
11068 **
11069 ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
11070 ** [database connection] D to disconnect from database S and then
11071 ** reopen S as an in-memory database based on the serialization
11072 ** contained in P. If S is a NULL pointer, the main database is
11073 ** used. The serialized database P is N bytes in size. M is the size
11074 ** of the buffer P, which might be larger than N. If M is larger than
11075 ** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then
11076 ** SQLite is permitted to add content to the in-memory database as
11077 ** long as the total size does not exceed M bytes.
11078 **
11079 ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
11080 ** invoke sqlite3_free() on the serialization buffer when the database
11081 ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
11082 ** SQLite will try to increase the buffer size using sqlite3_realloc64()
@@ -11052,10 +11139,56 @@
11139 */
11140 #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
11141 #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
11142 #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
11143
11144 /*
11145 ** CAPI3REF: Bind array values to the CARRAY table-valued function
11146 **
11147 ** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to
11148 ** one of the first argument of the [carray() table-valued function]. The
11149 ** S parameter is a pointer to the [prepared statement] that uses the carray()
11150 ** functions. I is the parameter index to be bound. P is a pointer to the
11151 ** array to be bound, and N is the number of eements in the array. The
11152 ** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
11153 ** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to
11154 ** indicate the datatype of the array being bound. The X argument is not a
11155 ** NULL pointer, then SQLite will invoke the function X on the P parameter
11156 ** after it has finished using P.
11157 */
11158 SQLITE_API SQLITE_API int sqlite3_carray_bind(
11159 sqlite3_stmt *pStmt, /* Statement to be bound */
11160 int i, /* Parameter index */
11161 void *aData, /* Pointer to array data */
11162 int nData, /* Number of data elements */
11163 int mFlags, /* CARRAY flags */
11164 void (*xDel)(void*) /* Destructor for aData */
11165 );
11166
11167 /*
11168 ** CAPI3REF: Datatypes for the CARRAY table-valued funtion
11169 **
11170 ** The fifth argument to the [sqlite3_carray_bind()] interface musts be
11171 ** one of the following constants, to specify the datatype of the array
11172 ** that is being bound into the [carray table-valued function].
11173 */
11174 #define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */
11175 #define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */
11176 #define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */
11177 #define SQLITE_CARRAY_TEXT 3 /* Data is char* */
11178 #define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */
11179
11180 /*
11181 ** Versions of the above #defines that omit the initial SQLITE_, for
11182 ** legacy compatibility.
11183 */
11184 #define CARRAY_INT32 0 /* Data is 32-bit signed integers */
11185 #define CARRAY_INT64 1 /* Data is 64-bit signed integers */
11186 #define CARRAY_DOUBLE 2 /* Data is doubles */
11187 #define CARRAY_TEXT 3 /* Data is char* */
11188 #define CARRAY_BLOB 4 /* Data is struct iovec */
11189
11190 /*
11191 ** Undo the hack that converts floating point types to integer for
11192 ** builds on processors without floating point support.
11193 */
11194 #ifdef SQLITE_OMIT_FLOATING_POINT
@@ -12310,10 +12443,19 @@
12443 ** CAPI3REF: Apply A Changeset To A Database
12444 **
12445 ** Apply a changeset or patchset to a database. These functions attempt to
12446 ** update the "main" database attached to handle db with the changes found in
12447 ** the changeset passed via the second and third arguments.
12448 **
12449 ** All changes made by these functions are enclosed in a savepoint transaction.
12450 ** If any other error (aside from a constraint failure when attempting to
12451 ** write to the target database) occurs, then the savepoint transaction is
12452 ** rolled back, restoring the target database to its original state, and an
12453 ** SQLite error code returned. Additionally, starting with version 3.51.0,
12454 ** an error code and error message that may be accessed using the
12455 ** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database
12456 ** handle.
12457 **
12458 ** The fourth argument (xFilter) passed to these functions is the "filter
12459 ** callback". This may be passed NULL, in which case all changes in the
12460 ** changeset are applied to the database. For sqlite3changeset_apply() and
12461 ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
@@ -12448,16 +12590,10 @@
12590 ** It is safe to execute SQL statements, including those that write to the
12591 ** table that the callback related to, from within the xConflict callback.
12592 ** This can be used to further customize the application's conflict
12593 ** resolution strategy.
12594 **
 
 
 
 
 
 
12595 ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
12596 ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
12597 ** may set (*ppRebase) to point to a "rebase" that may be used with the
12598 ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
12599 ** is set to the size of the buffer in bytes. It is the responsibility of the
12600
+2 -2
--- src/add.c
+++ src/add.c
@@ -1006,12 +1006,12 @@
10061006
10071007
/*
10081008
** COMMAND: mv
10091009
** COMMAND: rename*
10101010
**
1011
-** Usage: %fossil mv|rename OLDNAME NEWNAME
1012
-** or: %fossil mv|rename OLDNAME... DIR
1011
+** Usage: %fossil mv|rename ?OPTIONS? OLDNAME NEWNAME
1012
+** or: %fossil mv|rename ?OPTIONS? OLDNAME... DIR
10131013
**
10141014
** Move or rename one or more files or directories within the repository tree.
10151015
** You can either rename a file or directory or move it to another subdirectory.
10161016
**
10171017
** The 'mv' command does NOT normally rename or move the files on disk.
10181018
--- src/add.c
+++ src/add.c
@@ -1006,12 +1006,12 @@
1006
1007 /*
1008 ** COMMAND: mv
1009 ** COMMAND: rename*
1010 **
1011 ** Usage: %fossil mv|rename OLDNAME NEWNAME
1012 ** or: %fossil mv|rename OLDNAME... DIR
1013 **
1014 ** Move or rename one or more files or directories within the repository tree.
1015 ** You can either rename a file or directory or move it to another subdirectory.
1016 **
1017 ** The 'mv' command does NOT normally rename or move the files on disk.
1018
--- src/add.c
+++ src/add.c
@@ -1006,12 +1006,12 @@
1006
1007 /*
1008 ** COMMAND: mv
1009 ** COMMAND: rename*
1010 **
1011 ** Usage: %fossil mv|rename ?OPTIONS? OLDNAME NEWNAME
1012 ** or: %fossil mv|rename ?OPTIONS? OLDNAME... DIR
1013 **
1014 ** Move or rename one or more files or directories within the repository tree.
1015 ** You can either rename a file or directory or move it to another subdirectory.
1016 **
1017 ** The 'mv' command does NOT normally rename or move the files on disk.
1018
+80 -25
--- src/branch.c
+++ src/branch.c
@@ -843,10 +843,11 @@
843843
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
844844
style_set_current_feature("branch");
845845
style_header("Branches");
846846
style_adunit_config(ADUNIT_RIGHT_OK);
847847
style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
848
+
848849
login_anonymous_available();
849850
850851
brlist_create_temp_table();
851852
db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
852853
rNow = db_double(0.0, "SELECT julianday('now')");
@@ -1019,40 +1020,47 @@
10191020
/*
10201021
** This routine is called while for each check-in that is rendered by
10211022
** the timeline of a "brlist" page. Add some additional hyperlinks
10221023
** to the end of the line.
10231024
*/
1024
-static void brtimeline_extra(int rid){
1025
- Stmt q;
1025
+static void brtimeline_extra(
1026
+ Stmt *pQuery, /* Current row of the timeline query */
1027
+ int tmFlags, /* Flags to www_print_timeline() */
1028
+ const char *zThisUser, /* Suppress links to this user */
1029
+ const char *zThisTag /* Suppress links to this tag */
1030
+){
1031
+ int rid;
1032
+ int tmFlagsNew;
1033
+ char *zBrName;
1034
+
1035
+ if( (tmFlags & TIMELINE_INLINE)!=0 ){
1036
+ tmFlagsNew = (tmFlags & ~TIMELINE_VIEWS) | TIMELINE_MODERN;
1037
+ cgi_printf("(");
1038
+ }else{
1039
+ tmFlagsNew = tmFlags;
1040
+ }
1041
+ timeline_extra(pQuery,tmFlagsNew,zThisUser,zThisTag);
1042
+
10261043
if( !g.perm.Hyperlink ) return;
1027
- db_prepare(&q,
1028
- "SELECT substr(tagname,5) FROM tagxref, tag"
1029
- " WHERE tagxref.rid=%d"
1030
- " AND tagxref.tagid=tag.tagid"
1031
- " AND tagxref.tagtype>0"
1032
- " AND tag.tagname GLOB 'sym-*'",
1033
- rid
1034
- );
1035
- while( db_step(&q)==SQLITE_ROW ){
1036
- const char *zTagName = db_column_text(&q, 0);
1037
- @ %z(href("%R/timeline?r=%T",zTagName))[timeline]</a>
1038
- }
1039
- db_finalize(&q);
1044
+ rid = db_column_int(pQuery,0);
1045
+ zBrName = branch_of_rid(rid);
1046
+ @ branch:&nbsp;<span class='timelineHash'>\
1047
+ @ %z(href("%R/timeline?r=%T",zBrName))%h(zBrName)</a></span>
1048
+ if( (tmFlags & TIMELINE_INLINE)!=0 ){
1049
+ cgi_printf(")");
1050
+ }
10401051
}
10411052
10421053
/*
10431054
** WEBPAGE: brtimeline
10441055
**
1045
-** Show a timeline of all branches
1056
+** List the first check of every branch, starting with the most recent
1057
+** and going backwards in time.
10461058
**
10471059
** Query parameters:
10481060
**
1049
-** ng No graph
1050
-** nohidden Hide check-ins with "hidden" tag
1051
-** onlyhidden Show only check-ins with "hidden" tag
1052
-** brbg Background color by branch name
1053
-** ubg Background color by user name
1061
+** ubg Color the graph by user, not by branch.
10541062
*/
10551063
void brtimeline_page(void){
10561064
Blob sql = empty_blob;
10571065
Stmt q;
10581066
int tmFlags; /* Timeline display flags */
@@ -1059,14 +1067,15 @@
10591067
int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
10601068
int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
10611069
10621070
login_check_credentials();
10631071
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1072
+ if( robot_restrict("timelineX") ) return;
10641073
10651074
style_set_current_feature("branch");
10661075
style_header("Branches");
1067
- style_submenu_element("List", "brlist");
1076
+ style_submenu_element("Branch List", "brlist");
10681077
login_anonymous_available();
10691078
timeline_ss_submenu();
10701079
cgi_check_for_malice();
10711080
@ <h2>The initial check-in for each branch:</h2>
10721081
blob_append(&sql, timeline_query_for_www(), -1);
@@ -1083,12 +1092,58 @@
10831092
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
10841093
blob_reset(&sql);
10851094
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
10861095
** many descenders to (off-screen) parents. */
10871096
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
1088
- if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
1089
- if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
1090
- if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
1097
+ if( PB("ubg")!=0 ){
1098
+ tmFlags |= TIMELINE_UCOLOR;
1099
+ }else{
1100
+ tmFlags |= TIMELINE_BRCOLOR;
1101
+ }
10911102
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
10921103
db_finalize(&q);
10931104
style_finish_page();
10941105
}
1106
+
1107
+/*
1108
+** Generate a multichoice submenu for the few recent active branches. zName is
1109
+** the query parameter used to select the current checkin. zCI is optional and
1110
+** represent the currently selected checkin, so if it is a checkin hash
1111
+** instead of a branch, it can be part of the multichoice menu.
1112
+*/
1113
+void generate_branch_submenu_multichoice(
1114
+ const char* zName, /* Query parameter name */
1115
+ const char* zCI /* Current checkin */
1116
+){
1117
+ Stmt q;
1118
+ const int brFlags = BRL_ORDERBY_MTIME | BRL_OPEN_ONLY;
1119
+ static const char *zBranchMenuList[32*2]; /* 2 per entries */
1120
+ const int nLimit = count(zBranchMenuList)/2;
1121
+ int i = 0;
1122
+
1123
+ if( zName == 0 ) zName = "ci";
1124
+
1125
+ branch_prepare_list_query(&q, brFlags, 0, nLimit, 0);
1126
+ zBranchMenuList[i++] = "";
1127
+ zBranchMenuList[i++] = "All Checkins";
1128
+
1129
+ if( zCI ){
1130
+ zCI = fossil_strdup(zCI);
1131
+ zBranchMenuList[i++] = zCI;
1132
+ zBranchMenuList[i++] = zCI;
1133
+ }
1134
+ /* If current checkin is not "tip", add it to the list */
1135
+ if( zCI==0 || strcmp(zCI, "tip") ){
1136
+ zBranchMenuList[i++] = "tip";
1137
+ zBranchMenuList[i++] = "tip";
1138
+ }
1139
+ while( i/2 < nLimit && db_step(&q)==SQLITE_ROW ){
1140
+ const char* zBr = fossil_strdup(db_column_text(&q, 0));
1141
+ /* zCI is already in the list, don't add it twice */
1142
+ if( zCI==0 || strcmp(zBr, zCI) ){
1143
+ zBranchMenuList[i++] = zBr;
1144
+ zBranchMenuList[i++] = zBr;
1145
+ }
1146
+ }
1147
+ db_finalize(&q);
1148
+ style_submenu_multichoice(zName, i/2, zBranchMenuList, 0);
1149
+}
10951150
--- src/branch.c
+++ src/branch.c
@@ -843,10 +843,11 @@
843 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
844 style_set_current_feature("branch");
845 style_header("Branches");
846 style_adunit_config(ADUNIT_RIGHT_OK);
847 style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
 
848 login_anonymous_available();
849
850 brlist_create_temp_table();
851 db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
852 rNow = db_double(0.0, "SELECT julianday('now')");
@@ -1019,40 +1020,47 @@
1019 /*
1020 ** This routine is called while for each check-in that is rendered by
1021 ** the timeline of a "brlist" page. Add some additional hyperlinks
1022 ** to the end of the line.
1023 */
1024 static void brtimeline_extra(int rid){
1025 Stmt q;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1026 if( !g.perm.Hyperlink ) return;
1027 db_prepare(&q,
1028 "SELECT substr(tagname,5) FROM tagxref, tag"
1029 " WHERE tagxref.rid=%d"
1030 " AND tagxref.tagid=tag.tagid"
1031 " AND tagxref.tagtype>0"
1032 " AND tag.tagname GLOB 'sym-*'",
1033 rid
1034 );
1035 while( db_step(&q)==SQLITE_ROW ){
1036 const char *zTagName = db_column_text(&q, 0);
1037 @ %z(href("%R/timeline?r=%T",zTagName))[timeline]</a>
1038 }
1039 db_finalize(&q);
1040 }
1041
1042 /*
1043 ** WEBPAGE: brtimeline
1044 **
1045 ** Show a timeline of all branches
 
1046 **
1047 ** Query parameters:
1048 **
1049 ** ng No graph
1050 ** nohidden Hide check-ins with "hidden" tag
1051 ** onlyhidden Show only check-ins with "hidden" tag
1052 ** brbg Background color by branch name
1053 ** ubg Background color by user name
1054 */
1055 void brtimeline_page(void){
1056 Blob sql = empty_blob;
1057 Stmt q;
1058 int tmFlags; /* Timeline display flags */
@@ -1059,14 +1067,15 @@
1059 int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
1060 int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
1061
1062 login_check_credentials();
1063 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1064
1065 style_set_current_feature("branch");
1066 style_header("Branches");
1067 style_submenu_element("List", "brlist");
1068 login_anonymous_available();
1069 timeline_ss_submenu();
1070 cgi_check_for_malice();
1071 @ <h2>The initial check-in for each branch:</h2>
1072 blob_append(&sql, timeline_query_for_www(), -1);
@@ -1083,12 +1092,58 @@
1083 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
1084 blob_reset(&sql);
1085 /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
1086 ** many descenders to (off-screen) parents. */
1087 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
1088 if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
1089 if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
1090 if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
 
 
1091 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
1092 db_finalize(&q);
1093 style_finish_page();
1094 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1095
--- src/branch.c
+++ src/branch.c
@@ -843,10 +843,11 @@
843 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
844 style_set_current_feature("branch");
845 style_header("Branches");
846 style_adunit_config(ADUNIT_RIGHT_OK);
847 style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
848
849 login_anonymous_available();
850
851 brlist_create_temp_table();
852 db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
853 rNow = db_double(0.0, "SELECT julianday('now')");
@@ -1019,40 +1020,47 @@
1020 /*
1021 ** This routine is called while for each check-in that is rendered by
1022 ** the timeline of a "brlist" page. Add some additional hyperlinks
1023 ** to the end of the line.
1024 */
1025 static void brtimeline_extra(
1026 Stmt *pQuery, /* Current row of the timeline query */
1027 int tmFlags, /* Flags to www_print_timeline() */
1028 const char *zThisUser, /* Suppress links to this user */
1029 const char *zThisTag /* Suppress links to this tag */
1030 ){
1031 int rid;
1032 int tmFlagsNew;
1033 char *zBrName;
1034
1035 if( (tmFlags & TIMELINE_INLINE)!=0 ){
1036 tmFlagsNew = (tmFlags & ~TIMELINE_VIEWS) | TIMELINE_MODERN;
1037 cgi_printf("(");
1038 }else{
1039 tmFlagsNew = tmFlags;
1040 }
1041 timeline_extra(pQuery,tmFlagsNew,zThisUser,zThisTag);
1042
1043 if( !g.perm.Hyperlink ) return;
1044 rid = db_column_int(pQuery,0);
1045 zBrName = branch_of_rid(rid);
1046 @ branch:&nbsp;<span class='timelineHash'>\
1047 @ %z(href("%R/timeline?r=%T",zBrName))%h(zBrName)</a></span>
1048 if( (tmFlags & TIMELINE_INLINE)!=0 ){
1049 cgi_printf(")");
1050 }
 
 
 
 
 
 
1051 }
1052
1053 /*
1054 ** WEBPAGE: brtimeline
1055 **
1056 ** List the first check of every branch, starting with the most recent
1057 ** and going backwards in time.
1058 **
1059 ** Query parameters:
1060 **
1061 ** ubg Color the graph by user, not by branch.
 
 
 
 
1062 */
1063 void brtimeline_page(void){
1064 Blob sql = empty_blob;
1065 Stmt q;
1066 int tmFlags; /* Timeline display flags */
@@ -1059,14 +1067,15 @@
1067 int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
1068 int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
1069
1070 login_check_credentials();
1071 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1072 if( robot_restrict("timelineX") ) return;
1073
1074 style_set_current_feature("branch");
1075 style_header("Branches");
1076 style_submenu_element("Branch List", "brlist");
1077 login_anonymous_available();
1078 timeline_ss_submenu();
1079 cgi_check_for_malice();
1080 @ <h2>The initial check-in for each branch:</h2>
1081 blob_append(&sql, timeline_query_for_www(), -1);
@@ -1083,12 +1092,58 @@
1092 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
1093 blob_reset(&sql);
1094 /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
1095 ** many descenders to (off-screen) parents. */
1096 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
1097 if( PB("ubg")!=0 ){
1098 tmFlags |= TIMELINE_UCOLOR;
1099 }else{
1100 tmFlags |= TIMELINE_BRCOLOR;
1101 }
1102 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
1103 db_finalize(&q);
1104 style_finish_page();
1105 }
1106
1107 /*
1108 ** Generate a multichoice submenu for the few recent active branches. zName is
1109 ** the query parameter used to select the current checkin. zCI is optional and
1110 ** represent the currently selected checkin, so if it is a checkin hash
1111 ** instead of a branch, it can be part of the multichoice menu.
1112 */
1113 void generate_branch_submenu_multichoice(
1114 const char* zName, /* Query parameter name */
1115 const char* zCI /* Current checkin */
1116 ){
1117 Stmt q;
1118 const int brFlags = BRL_ORDERBY_MTIME | BRL_OPEN_ONLY;
1119 static const char *zBranchMenuList[32*2]; /* 2 per entries */
1120 const int nLimit = count(zBranchMenuList)/2;
1121 int i = 0;
1122
1123 if( zName == 0 ) zName = "ci";
1124
1125 branch_prepare_list_query(&q, brFlags, 0, nLimit, 0);
1126 zBranchMenuList[i++] = "";
1127 zBranchMenuList[i++] = "All Checkins";
1128
1129 if( zCI ){
1130 zCI = fossil_strdup(zCI);
1131 zBranchMenuList[i++] = zCI;
1132 zBranchMenuList[i++] = zCI;
1133 }
1134 /* If current checkin is not "tip", add it to the list */
1135 if( zCI==0 || strcmp(zCI, "tip") ){
1136 zBranchMenuList[i++] = "tip";
1137 zBranchMenuList[i++] = "tip";
1138 }
1139 while( i/2 < nLimit && db_step(&q)==SQLITE_ROW ){
1140 const char* zBr = fossil_strdup(db_column_text(&q, 0));
1141 /* zCI is already in the list, don't add it twice */
1142 if( zCI==0 || strcmp(zBr, zCI) ){
1143 zBranchMenuList[i++] = zBr;
1144 zBranchMenuList[i++] = zBr;
1145 }
1146 }
1147 db_finalize(&q);
1148 style_submenu_multichoice(zName, i/2, zBranchMenuList, 0);
1149 }
1150
+24 -31
--- src/browse.c
+++ src/browse.c
@@ -171,12 +171,10 @@
171171
const char *zCI = P("ci");
172172
int rid = 0;
173173
char *zUuid = 0;
174174
Manifest *pM = 0;
175175
const char *zSubdirLink;
176
- int linkTrunk = 1;
177
- int linkTip = 1;
178176
HQuery sURI;
179177
int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
180178
int isBranchCI = 0; /* True if ci= refers to a branch name */
181179
char *zHeader = 0;
182180
const char *zRegexp; /* The re= query parameter */
@@ -198,13 +196,10 @@
198196
*/
199197
if( bDocDir && zCI==0 ) zCI = "trunk";
200198
if( zCI ){
201199
pM = manifest_get_by_name(zCI, &rid);
202200
if( pM ){
203
- int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
204
- linkTrunk = trunkRid && rid != trunkRid;
205
- linkTip = rid != symbolic_name_to_rid("tip", "ci");
206201
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
207202
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
208203
isBranchCI = branch_includes_uuid(zCI, zUuid);
209204
if( bDocDir ) zCI = mprintf("%S", zUuid);
210205
Th_StoreUnsafe("current_checkin", zCI);
@@ -234,10 +229,13 @@
234229
}else{
235230
zMatch = "";
236231
}
237232
style_header("%s", zHeader);
238233
fossil_free(zHeader);
234
+ if( rid && zD==0 && zMatch[0]==0 && g.perm.Zip ){
235
+ style_submenu_element("Download","%R/rchvdwnld/%!S",zUuid);
236
+ }
239237
style_adunit_config(ADUNIT_RIGHT_OK);
240238
sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
241239
pathelementFunc, 0, 0);
242240
url_initialize(&sURI, "dir");
243241
cgi_check_for_malice();
@@ -283,25 +281,22 @@
283281
}
284282
}else{
285283
@ in any check-in</h2>
286284
zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
287285
}
288
- if( linkTrunk && !bDocDir ){
289
- style_submenu_element("Trunk", "%s",
290
- url_render(&sURI, "ci", "trunk", 0, 0));
291
- }
292
- if( linkTip && !bDocDir ){
293
- style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
294
- }
295286
if( zD && !bDocDir ){
296287
style_submenu_element("History","%R/timeline?chng=%T/*", zD);
297288
}
298289
if( !bDocDir ){
299
- style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
300290
style_submenu_element("Tree-View", "%s",
301291
url_render(&sURI, "type", "tree", 0, 0));
302292
}
293
+
294
+ if( !bDocDir ){
295
+ /* Generate the Branch list submenu */
296
+ generate_branch_submenu_multichoice("ci", zCI);
297
+ }
303298
304299
/* Compute the temporary table "localfiles" containing the names
305300
** of all files and subdirectories in the zD[] directory.
306301
**
307302
** Subdirectory names begin with "/". This causes them to sort
@@ -705,12 +700,10 @@
705700
Manifest *pM = 0;
706701
double rNow = 0;
707702
char *zNow = 0;
708703
int useMtime = atoi(PD("mtime","0"));
709704
int sortOrder = atoi(PD("sort",useMtime?"1":"0"));
710
- int linkTrunk = 1; /* include link to "trunk" */
711
- int linkTip = 1; /* include link to "tip" */
712705
const char *zRE; /* the value for the re=REGEXP query parameter */
713706
const char *zObjType; /* "files" by default or "folders" for "nofiles" */
714707
char *zREx = ""; /* Extra parameters for path hyperlinks */
715708
ReCompiled *pRE = 0; /* Compiled regular expression */
716709
FileTreeNode *p; /* One line of the tree */
@@ -747,11 +740,11 @@
747740
}
748741
749742
/* If a regular expression is specified, compile it */
750743
zRE = P("re");
751744
if( zRE ){
752
- re_compile(&pRE, zRE, 0);
745
+ fossil_re_compile(&pRE, zRE, 0);
753746
zREx = mprintf("&re=%T", zRE);
754747
}
755748
cgi_check_for_malice();
756749
757750
/* If the name= parameter is an empty string, make it a NULL pointer */
@@ -762,13 +755,10 @@
762755
** files from all check-ins to be displayed.
763756
*/
764757
if( zCI ){
765758
pM = manifest_get_by_name(zCI, &rid);
766759
if( pM ){
767
- int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
768
- linkTrunk = trunkRid && rid != trunkRid;
769
- linkTip = rid != symbolic_name_to_rid("tip", "ci");
770760
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
771761
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
772762
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
773763
" FROM event WHERE objid=%d", rid);
774764
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
@@ -781,10 +771,13 @@
781771
if( zCI==0 ){
782772
rNow = db_double(0.0, "SELECT max(mtime) FROM event");
783773
zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
784774
}
785775
776
+ /* Generate the Branch list submenu */
777
+ generate_branch_submenu_multichoice("ci", zCI);
778
+
786779
assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
787780
if( zD==0 ){
788781
if( zCI ){
789782
zHeader = mprintf("Top-level Files of %s", zCI);
790783
}else{
@@ -818,24 +811,19 @@
818811
"2", "Sort By Size"
819812
};
820813
style_submenu_multichoice("sort", 3, sort_orders, 0);
821814
}
822815
if( zCI ){
823
- style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
824816
if( nD==0 && !showDirOnly ){
825817
style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
826818
}
827819
}
828
- if( linkTrunk ){
829
- style_submenu_element("Trunk", "%s",
830
- url_render(&sURI, "ci", "trunk", 0, 0));
831
- }
832
- if( linkTip ){
833
- style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
834
- }
835820
style_submenu_element("Flat-View", "%s",
836821
url_render(&sURI, "type", "flat", 0, 0));
822
+ if( rid && zD==0 && zRE==0 && !showDirOnly && g.perm.Zip ){
823
+ style_submenu_element("Download","%R/rchvdwnld/%!S", zUuid);
824
+ }
837825
838826
/* Compute the file hierarchy.
839827
*/
840828
if( zCI ){
841829
Stmt q;
@@ -1038,11 +1026,12 @@
10381026
@ CREATE TABLE IF NOT EXISTS temp.fileage(
10391027
@ fnid INTEGER PRIMARY KEY,
10401028
@ fid INTEGER,
10411029
@ mid INTEGER,
10421030
@ mtime DATETIME,
1043
-@ pathname TEXT
1031
+@ pathname TEXT,
1032
+@ uuid TEXT
10441033
@ );
10451034
@ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;
10461035
;
10471036
10481037
static const char zComputeFileAgeRun[] =
@@ -1050,12 +1039,13 @@
10501039
@ ckin(x) AS (VALUES(:ckin)
10511040
@ UNION
10521041
@ SELECT plink.pid
10531042
@ FROM ckin, plink
10541043
@ WHERE plink.cid=ckin.x)
1055
-@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
1056
-@ SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name
1044
+@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname, uuid)
1045
+@ SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name,
1046
+@ foci.uuid
10571047
@ FROM foci, filename, blob, mlink, event
10581048
@ WHERE foci.checkinID=:ckin
10591049
@ AND foci.filename GLOB :glob
10601050
@ AND filename.name=foci.filename
10611051
@ AND blob.uuid=foci.uuid
@@ -1162,11 +1152,10 @@
11621152
int showId = PB("showid");
11631153
Stmt q1, q2;
11641154
double baseTime;
11651155
login_check_credentials();
11661156
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1167
- if( exclude_spiders(0) ) return;
11681157
zName = P("name");
11691158
if( zName==0 ) zName = "tip";
11701159
rid = symbolic_name_to_rid(zName, "ci");
11711160
if( rid==0 ){
11721161
fossil_fatal("not a valid check-in: %s", zName);
@@ -1175,10 +1164,14 @@
11751164
isBranchCI = branch_includes_uuid(zName,zUuid);
11761165
baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
11771166
zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
11781167
" WHERE objid=%d", rid);
11791168
style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
1169
+
1170
+ /* Generate the Branch list submenu */
1171
+ generate_branch_submenu_multichoice("name", zName);
1172
+
11801173
style_header("File Ages");
11811174
zGlob = P("glob");
11821175
cgi_check_for_malice();
11831176
compute_fileage(rid,zGlob);
11841177
db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
11851178
--- src/browse.c
+++ src/browse.c
@@ -171,12 +171,10 @@
171 const char *zCI = P("ci");
172 int rid = 0;
173 char *zUuid = 0;
174 Manifest *pM = 0;
175 const char *zSubdirLink;
176 int linkTrunk = 1;
177 int linkTip = 1;
178 HQuery sURI;
179 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
180 int isBranchCI = 0; /* True if ci= refers to a branch name */
181 char *zHeader = 0;
182 const char *zRegexp; /* The re= query parameter */
@@ -198,13 +196,10 @@
198 */
199 if( bDocDir && zCI==0 ) zCI = "trunk";
200 if( zCI ){
201 pM = manifest_get_by_name(zCI, &rid);
202 if( pM ){
203 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
204 linkTrunk = trunkRid && rid != trunkRid;
205 linkTip = rid != symbolic_name_to_rid("tip", "ci");
206 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
207 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
208 isBranchCI = branch_includes_uuid(zCI, zUuid);
209 if( bDocDir ) zCI = mprintf("%S", zUuid);
210 Th_StoreUnsafe("current_checkin", zCI);
@@ -234,10 +229,13 @@
234 }else{
235 zMatch = "";
236 }
237 style_header("%s", zHeader);
238 fossil_free(zHeader);
 
 
 
239 style_adunit_config(ADUNIT_RIGHT_OK);
240 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
241 pathelementFunc, 0, 0);
242 url_initialize(&sURI, "dir");
243 cgi_check_for_malice();
@@ -283,25 +281,22 @@
283 }
284 }else{
285 @ in any check-in</h2>
286 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
287 }
288 if( linkTrunk && !bDocDir ){
289 style_submenu_element("Trunk", "%s",
290 url_render(&sURI, "ci", "trunk", 0, 0));
291 }
292 if( linkTip && !bDocDir ){
293 style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
294 }
295 if( zD && !bDocDir ){
296 style_submenu_element("History","%R/timeline?chng=%T/*", zD);
297 }
298 if( !bDocDir ){
299 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
300 style_submenu_element("Tree-View", "%s",
301 url_render(&sURI, "type", "tree", 0, 0));
302 }
 
 
 
 
 
303
304 /* Compute the temporary table "localfiles" containing the names
305 ** of all files and subdirectories in the zD[] directory.
306 **
307 ** Subdirectory names begin with "/". This causes them to sort
@@ -705,12 +700,10 @@
705 Manifest *pM = 0;
706 double rNow = 0;
707 char *zNow = 0;
708 int useMtime = atoi(PD("mtime","0"));
709 int sortOrder = atoi(PD("sort",useMtime?"1":"0"));
710 int linkTrunk = 1; /* include link to "trunk" */
711 int linkTip = 1; /* include link to "tip" */
712 const char *zRE; /* the value for the re=REGEXP query parameter */
713 const char *zObjType; /* "files" by default or "folders" for "nofiles" */
714 char *zREx = ""; /* Extra parameters for path hyperlinks */
715 ReCompiled *pRE = 0; /* Compiled regular expression */
716 FileTreeNode *p; /* One line of the tree */
@@ -747,11 +740,11 @@
747 }
748
749 /* If a regular expression is specified, compile it */
750 zRE = P("re");
751 if( zRE ){
752 re_compile(&pRE, zRE, 0);
753 zREx = mprintf("&re=%T", zRE);
754 }
755 cgi_check_for_malice();
756
757 /* If the name= parameter is an empty string, make it a NULL pointer */
@@ -762,13 +755,10 @@
762 ** files from all check-ins to be displayed.
763 */
764 if( zCI ){
765 pM = manifest_get_by_name(zCI, &rid);
766 if( pM ){
767 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
768 linkTrunk = trunkRid && rid != trunkRid;
769 linkTip = rid != symbolic_name_to_rid("tip", "ci");
770 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
771 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
772 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
773 " FROM event WHERE objid=%d", rid);
774 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
@@ -781,10 +771,13 @@
781 if( zCI==0 ){
782 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
783 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
784 }
785
 
 
 
786 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
787 if( zD==0 ){
788 if( zCI ){
789 zHeader = mprintf("Top-level Files of %s", zCI);
790 }else{
@@ -818,24 +811,19 @@
818 "2", "Sort By Size"
819 };
820 style_submenu_multichoice("sort", 3, sort_orders, 0);
821 }
822 if( zCI ){
823 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
824 if( nD==0 && !showDirOnly ){
825 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
826 }
827 }
828 if( linkTrunk ){
829 style_submenu_element("Trunk", "%s",
830 url_render(&sURI, "ci", "trunk", 0, 0));
831 }
832 if( linkTip ){
833 style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
834 }
835 style_submenu_element("Flat-View", "%s",
836 url_render(&sURI, "type", "flat", 0, 0));
 
 
 
837
838 /* Compute the file hierarchy.
839 */
840 if( zCI ){
841 Stmt q;
@@ -1038,11 +1026,12 @@
1038 @ CREATE TABLE IF NOT EXISTS temp.fileage(
1039 @ fnid INTEGER PRIMARY KEY,
1040 @ fid INTEGER,
1041 @ mid INTEGER,
1042 @ mtime DATETIME,
1043 @ pathname TEXT
 
1044 @ );
1045 @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;
1046 ;
1047
1048 static const char zComputeFileAgeRun[] =
@@ -1050,12 +1039,13 @@
1050 @ ckin(x) AS (VALUES(:ckin)
1051 @ UNION
1052 @ SELECT plink.pid
1053 @ FROM ckin, plink
1054 @ WHERE plink.cid=ckin.x)
1055 @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
1056 @ SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name
 
1057 @ FROM foci, filename, blob, mlink, event
1058 @ WHERE foci.checkinID=:ckin
1059 @ AND foci.filename GLOB :glob
1060 @ AND filename.name=foci.filename
1061 @ AND blob.uuid=foci.uuid
@@ -1162,11 +1152,10 @@
1162 int showId = PB("showid");
1163 Stmt q1, q2;
1164 double baseTime;
1165 login_check_credentials();
1166 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1167 if( exclude_spiders(0) ) return;
1168 zName = P("name");
1169 if( zName==0 ) zName = "tip";
1170 rid = symbolic_name_to_rid(zName, "ci");
1171 if( rid==0 ){
1172 fossil_fatal("not a valid check-in: %s", zName);
@@ -1175,10 +1164,14 @@
1175 isBranchCI = branch_includes_uuid(zName,zUuid);
1176 baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
1177 zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
1178 " WHERE objid=%d", rid);
1179 style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
 
 
 
 
1180 style_header("File Ages");
1181 zGlob = P("glob");
1182 cgi_check_for_malice();
1183 compute_fileage(rid,zGlob);
1184 db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
1185
--- src/browse.c
+++ src/browse.c
@@ -171,12 +171,10 @@
171 const char *zCI = P("ci");
172 int rid = 0;
173 char *zUuid = 0;
174 Manifest *pM = 0;
175 const char *zSubdirLink;
 
 
176 HQuery sURI;
177 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
178 int isBranchCI = 0; /* True if ci= refers to a branch name */
179 char *zHeader = 0;
180 const char *zRegexp; /* The re= query parameter */
@@ -198,13 +196,10 @@
196 */
197 if( bDocDir && zCI==0 ) zCI = "trunk";
198 if( zCI ){
199 pM = manifest_get_by_name(zCI, &rid);
200 if( pM ){
 
 
 
201 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
202 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
203 isBranchCI = branch_includes_uuid(zCI, zUuid);
204 if( bDocDir ) zCI = mprintf("%S", zUuid);
205 Th_StoreUnsafe("current_checkin", zCI);
@@ -234,10 +229,13 @@
229 }else{
230 zMatch = "";
231 }
232 style_header("%s", zHeader);
233 fossil_free(zHeader);
234 if( rid && zD==0 && zMatch[0]==0 && g.perm.Zip ){
235 style_submenu_element("Download","%R/rchvdwnld/%!S",zUuid);
236 }
237 style_adunit_config(ADUNIT_RIGHT_OK);
238 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
239 pathelementFunc, 0, 0);
240 url_initialize(&sURI, "dir");
241 cgi_check_for_malice();
@@ -283,25 +281,22 @@
281 }
282 }else{
283 @ in any check-in</h2>
284 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
285 }
 
 
 
 
 
 
 
286 if( zD && !bDocDir ){
287 style_submenu_element("History","%R/timeline?chng=%T/*", zD);
288 }
289 if( !bDocDir ){
 
290 style_submenu_element("Tree-View", "%s",
291 url_render(&sURI, "type", "tree", 0, 0));
292 }
293
294 if( !bDocDir ){
295 /* Generate the Branch list submenu */
296 generate_branch_submenu_multichoice("ci", zCI);
297 }
298
299 /* Compute the temporary table "localfiles" containing the names
300 ** of all files and subdirectories in the zD[] directory.
301 **
302 ** Subdirectory names begin with "/". This causes them to sort
@@ -705,12 +700,10 @@
700 Manifest *pM = 0;
701 double rNow = 0;
702 char *zNow = 0;
703 int useMtime = atoi(PD("mtime","0"));
704 int sortOrder = atoi(PD("sort",useMtime?"1":"0"));
 
 
705 const char *zRE; /* the value for the re=REGEXP query parameter */
706 const char *zObjType; /* "files" by default or "folders" for "nofiles" */
707 char *zREx = ""; /* Extra parameters for path hyperlinks */
708 ReCompiled *pRE = 0; /* Compiled regular expression */
709 FileTreeNode *p; /* One line of the tree */
@@ -747,11 +740,11 @@
740 }
741
742 /* If a regular expression is specified, compile it */
743 zRE = P("re");
744 if( zRE ){
745 fossil_re_compile(&pRE, zRE, 0);
746 zREx = mprintf("&re=%T", zRE);
747 }
748 cgi_check_for_malice();
749
750 /* If the name= parameter is an empty string, make it a NULL pointer */
@@ -762,13 +755,10 @@
755 ** files from all check-ins to be displayed.
756 */
757 if( zCI ){
758 pM = manifest_get_by_name(zCI, &rid);
759 if( pM ){
 
 
 
760 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
761 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
762 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
763 " FROM event WHERE objid=%d", rid);
764 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
@@ -781,10 +771,13 @@
771 if( zCI==0 ){
772 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
773 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
774 }
775
776 /* Generate the Branch list submenu */
777 generate_branch_submenu_multichoice("ci", zCI);
778
779 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
780 if( zD==0 ){
781 if( zCI ){
782 zHeader = mprintf("Top-level Files of %s", zCI);
783 }else{
@@ -818,24 +811,19 @@
811 "2", "Sort By Size"
812 };
813 style_submenu_multichoice("sort", 3, sort_orders, 0);
814 }
815 if( zCI ){
 
816 if( nD==0 && !showDirOnly ){
817 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
818 }
819 }
 
 
 
 
 
 
 
820 style_submenu_element("Flat-View", "%s",
821 url_render(&sURI, "type", "flat", 0, 0));
822 if( rid && zD==0 && zRE==0 && !showDirOnly && g.perm.Zip ){
823 style_submenu_element("Download","%R/rchvdwnld/%!S", zUuid);
824 }
825
826 /* Compute the file hierarchy.
827 */
828 if( zCI ){
829 Stmt q;
@@ -1038,11 +1026,12 @@
1026 @ CREATE TABLE IF NOT EXISTS temp.fileage(
1027 @ fnid INTEGER PRIMARY KEY,
1028 @ fid INTEGER,
1029 @ mid INTEGER,
1030 @ mtime DATETIME,
1031 @ pathname TEXT,
1032 @ uuid TEXT
1033 @ );
1034 @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;
1035 ;
1036
1037 static const char zComputeFileAgeRun[] =
@@ -1050,12 +1039,13 @@
1039 @ ckin(x) AS (VALUES(:ckin)
1040 @ UNION
1041 @ SELECT plink.pid
1042 @ FROM ckin, plink
1043 @ WHERE plink.cid=ckin.x)
1044 @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname, uuid)
1045 @ SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name,
1046 @ foci.uuid
1047 @ FROM foci, filename, blob, mlink, event
1048 @ WHERE foci.checkinID=:ckin
1049 @ AND foci.filename GLOB :glob
1050 @ AND filename.name=foci.filename
1051 @ AND blob.uuid=foci.uuid
@@ -1162,11 +1152,10 @@
1152 int showId = PB("showid");
1153 Stmt q1, q2;
1154 double baseTime;
1155 login_check_credentials();
1156 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1157 zName = P("name");
1158 if( zName==0 ) zName = "tip";
1159 rid = symbolic_name_to_rid(zName, "ci");
1160 if( rid==0 ){
1161 fossil_fatal("not a valid check-in: %s", zName);
@@ -1175,10 +1164,14 @@
1164 isBranchCI = branch_includes_uuid(zName,zUuid);
1165 baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
1166 zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
1167 " WHERE objid=%d", rid);
1168 style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
1169
1170 /* Generate the Branch list submenu */
1171 generate_branch_submenu_multichoice("name", zName);
1172
1173 style_header("File Ages");
1174 zGlob = P("glob");
1175 cgi_check_for_malice();
1176 compute_fileage(rid,zGlob);
1177 db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
1178
+12 -8
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744744
(void)exclude_spiders(1);
745745
@ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746746
@ to see how HEX would be rendered in the current captcha font.
747747
@ <h2>Debug/Testing Values:</h2>
748748
@ <ul>
749
- @ <li> g.isHuman = %d(g.isHuman)
749
+ @ <li> g.isRobot = %d(g.isRobot)
750750
@ <li> g.zLogin = %h(g.zLogin)
751751
@ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752752
@ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753753
@ </ul>
754754
style_finish_page();
@@ -759,10 +759,18 @@
759759
@ %s(captcha_render(zPw))
760760
@ </pre>
761761
style_finish_page();
762762
}
763763
}
764
+
765
+/*
766
+** WEBPAGE: honeypot
767
+** This page is a honeypot for spiders and bots.
768
+*/
769
+void honeypot_page(void){
770
+ (void)exclude_spiders(0);
771
+}
764772
765773
/*
766774
** Check to see if the current request is coming from an agent that
767775
** self-identifies as a spider.
768776
**
@@ -776,27 +784,23 @@
776784
** If the bTest argument is non-zero, then show the captcha regardless of
777785
** how the agent identifies. This is used for testing only.
778786
*/
779787
int exclude_spiders(int bTest){
780788
if( !bTest ){
781
- if( g.isHuman ) return 0; /* This user has already proven human */
782789
if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
783790
if( login_cookie_wellformed() ){
784791
/* Logged into another member of the login group */
785792
return 0;
786793
}
787794
}
788795
789796
/* This appears to be a spider. Offer the captcha */
790797
style_set_current_feature("captcha");
791
- style_header("I think you are a robot");
798
+ style_header("Captcha");
792799
style_submenu_enable(0);
793800
@ <form method='POST' action='%R/ityaar'>
794
- @ <p>You seem like a robot.
795
- @
796
- @ <p>If you are human, you can prove that by solving the captcha below,
797
- @ after which you will be allowed to proceed.
801
+ @ <h2>Prove that you are human:
798802
if( bTest ){
799803
@ <input type="hidden" name="istest" value="1">
800804
}
801805
captcha_generate(3);
802806
@ </form>
@@ -830,11 +834,11 @@
830834
}
831835
cgi_append_header("X-Robot: 0\r\n");
832836
}
833837
login_redirect_to_g();
834838
}else{
835
- g.isHuman = 0;
839
+ g.isRobot = 1;
836840
(void)exclude_spiders(bTest);
837841
if( bTest ){
838842
@ <hr><p>Wrong code. Try again
839843
style_finish_page();
840844
}
841845
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744 (void)exclude_spiders(1);
745 @ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746 @ to see how HEX would be rendered in the current captcha font.
747 @ <h2>Debug/Testing Values:</h2>
748 @ <ul>
749 @ <li> g.isHuman = %d(g.isHuman)
750 @ <li> g.zLogin = %h(g.zLogin)
751 @ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752 @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753 @ </ul>
754 style_finish_page();
@@ -759,10 +759,18 @@
759 @ %s(captcha_render(zPw))
760 @ </pre>
761 style_finish_page();
762 }
763 }
 
 
 
 
 
 
 
 
764
765 /*
766 ** Check to see if the current request is coming from an agent that
767 ** self-identifies as a spider.
768 **
@@ -776,27 +784,23 @@
776 ** If the bTest argument is non-zero, then show the captcha regardless of
777 ** how the agent identifies. This is used for testing only.
778 */
779 int exclude_spiders(int bTest){
780 if( !bTest ){
781 if( g.isHuman ) return 0; /* This user has already proven human */
782 if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
783 if( login_cookie_wellformed() ){
784 /* Logged into another member of the login group */
785 return 0;
786 }
787 }
788
789 /* This appears to be a spider. Offer the captcha */
790 style_set_current_feature("captcha");
791 style_header("I think you are a robot");
792 style_submenu_enable(0);
793 @ <form method='POST' action='%R/ityaar'>
794 @ <p>You seem like a robot.
795 @
796 @ <p>If you are human, you can prove that by solving the captcha below,
797 @ after which you will be allowed to proceed.
798 if( bTest ){
799 @ <input type="hidden" name="istest" value="1">
800 }
801 captcha_generate(3);
802 @ </form>
@@ -830,11 +834,11 @@
830 }
831 cgi_append_header("X-Robot: 0\r\n");
832 }
833 login_redirect_to_g();
834 }else{
835 g.isHuman = 0;
836 (void)exclude_spiders(bTest);
837 if( bTest ){
838 @ <hr><p>Wrong code. Try again
839 style_finish_page();
840 }
841
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744 (void)exclude_spiders(1);
745 @ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746 @ to see how HEX would be rendered in the current captcha font.
747 @ <h2>Debug/Testing Values:</h2>
748 @ <ul>
749 @ <li> g.isRobot = %d(g.isRobot)
750 @ <li> g.zLogin = %h(g.zLogin)
751 @ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752 @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753 @ </ul>
754 style_finish_page();
@@ -759,10 +759,18 @@
759 @ %s(captcha_render(zPw))
760 @ </pre>
761 style_finish_page();
762 }
763 }
764
765 /*
766 ** WEBPAGE: honeypot
767 ** This page is a honeypot for spiders and bots.
768 */
769 void honeypot_page(void){
770 (void)exclude_spiders(0);
771 }
772
773 /*
774 ** Check to see if the current request is coming from an agent that
775 ** self-identifies as a spider.
776 **
@@ -776,27 +784,23 @@
784 ** If the bTest argument is non-zero, then show the captcha regardless of
785 ** how the agent identifies. This is used for testing only.
786 */
787 int exclude_spiders(int bTest){
788 if( !bTest ){
 
789 if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
790 if( login_cookie_wellformed() ){
791 /* Logged into another member of the login group */
792 return 0;
793 }
794 }
795
796 /* This appears to be a spider. Offer the captcha */
797 style_set_current_feature("captcha");
798 style_header("Captcha");
799 style_submenu_enable(0);
800 @ <form method='POST' action='%R/ityaar'>
801 @ <h2>Prove that you are human:
 
 
 
802 if( bTest ){
803 @ <input type="hidden" name="istest" value="1">
804 }
805 captcha_generate(3);
806 @ </form>
@@ -830,11 +834,11 @@
834 }
835 cgi_append_header("X-Robot: 0\r\n");
836 }
837 login_redirect_to_g();
838 }else{
839 g.isRobot = 1;
840 (void)exclude_spiders(bTest);
841 if( bTest ){
842 @ <hr><p>Wrong code. Try again
843 style_finish_page();
844 }
845
+82 -36
--- src/cgi.c
+++ src/cgi.c
@@ -241,11 +241,11 @@
241241
}
242242
243243
/*
244244
** Additional information used to form the HTTP reply
245245
*/
246
-static const char *zContentType = "text/html"; /* Content type of the reply */
246
+static const char *zReplyMimeType = "text/html"; /* Content type of the reply */
247247
static const char *zReplyStatus = "OK"; /* Reply status description */
248248
static int iReplyStatus = 200; /* Reply status code */
249249
static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
250250
static int rangeStart = 0; /* Start of Range: */
251251
static int rangeEnd = 0; /* End of Range: plus 1 */
@@ -256,11 +256,13 @@
256256
** The reply content type defaults to "text/html". It only needs to be
257257
** changed (by calling this routine) in the exceptional case where some
258258
** other content type is being returned.
259259
*/
260260
void cgi_set_content_type(const char *zType){
261
- zContentType = fossil_strdup(zType);
261
+ int i;
262
+ for(i=0; zType[i]>='+' && zType[i]<='z'; i++){}
263
+ zReplyMimeType = fossil_strndup(zType, i);
262264
}
263265
264266
/*
265267
** Erase any existing reply content. Replace is with a pNewContent.
266268
**
@@ -336,14 +338,14 @@
336338
if( g.fNoHttpCompress ) return 0;
337339
if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
338340
/* Maintenance note: this oddball structure is intended to make
339341
** adding new mimetypes to this list less of a performance hit than
340342
** doing a strcmp/glob over a growing set of compressible types. */
341
- switch(zContentType ? *zContentType : 0){
343
+ switch(zReplyMimeType ? *zReplyMimeType : 0){
342344
case (int)'a':
343
- if(0==fossil_strncmp("application/",zContentType,12)){
344
- const char * z = &zContentType[12];
345
+ if(0==fossil_strncmp("application/",zReplyMimeType,12)){
346
+ const char * z = &zReplyMimeType[12];
345347
switch(*z){
346348
case (int)'j':
347349
return fossil_strcmp("javascript", z)==0
348350
|| fossil_strcmp("json", z)==0;
349351
case (int)'w': return fossil_strcmp("wasm", z)==0;
@@ -354,14 +356,14 @@
354356
return sqlite3_strglob("*xml", z)==0;
355357
}
356358
}
357359
break;
358360
case (int)'i':
359
- return fossil_strcmp(zContentType, "image/svg+xml")==0
360
- || fossil_strcmp(zContentType, "image/vnd.microsoft.icon")==0;
361
+ return fossil_strcmp(zReplyMimeType, "image/svg+xml")==0
362
+ || fossil_strcmp(zReplyMimeType, "image/vnd.microsoft.icon")==0;
361363
case (int)'t':
362
- return fossil_strncmp(zContentType, "text/", 5)==0;
364
+ return fossil_strncmp(zReplyMimeType, "text/", 5)==0;
363365
}
364366
return 0;
365367
}
366368
367369
@@ -402,10 +404,12 @@
402404
}
403405
#ifdef FOSSIL_ENABLE_SSL
404406
return ssl_read_server(g.httpSSLConn, ptr, nmemb, 1);
405407
#else
406408
fossil_fatal("SSL not available");
409
+ /* NOT REACHED */
410
+ return 0;
407411
#endif
408412
}
409413
410414
/* Works like feof():
411415
**
@@ -449,22 +453,22 @@
449453
}
450454
451455
/*
452456
** Given a Content-Type value, returns a string suitable for appending
453457
** to the Content-Type header for adding (or not) the "; charset=..."
454
-** part. It returns an empty string for most types or if zContentType
458
+** part. It returns an empty string for most types or if zReplyMimeType
455459
** is NULL.
456460
**
457461
** See forum post f60dece061c364d1 for the discussions which lead to
458462
** this. Previously we always appended the charset, but WASM loaders
459463
** are pedantic and refuse to load any responses which have a
460464
** charset. Also, adding a charset is not strictly appropriate for
461465
** most types (and not required for many others which may ostensibly
462466
** benefit from one, as detailed in that forum post).
463467
*/
464
-static const char * content_type_charset(const char *zContentType){
465
- if(0==fossil_strncmp(zContentType,"text/",5)){
468
+static const char * content_type_charset(const char *zReplyMimeType){
469
+ if(0==fossil_strncmp(zReplyMimeType,"text/",5)){
466470
return "; charset=utf-8";
467471
}
468472
return "";
469473
}
470474
@@ -500,11 +504,11 @@
500504
assert( rangeEnd==0 );
501505
blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
502506
}
503507
if( etag_tag()[0]!=0
504508
&& iReplyStatus==200
505
- && strcmp(zContentType,"text/html")!=0
509
+ && strcmp(zReplyMimeType,"text/html")!=0
506510
){
507511
/* Do not cache HTML replies as those will have been generated and
508512
** will likely, therefore, contains a nonce and we want that nonce to
509513
** be different every time. */
510514
blob_appendf(&hdr, "ETag: \"%s\"\r\n", etag_tag());
@@ -542,13 +546,13 @@
542546
** These headers are probably best added by the web server hosting fossil as
543547
** a CGI script.
544548
*/
545549
546550
if( iReplyStatus!=304 ) {
547
- blob_appendf(&hdr, "Content-Type: %s%s\r\n", zContentType,
548
- content_type_charset(zContentType));
549
- if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
551
+ blob_appendf(&hdr, "Content-Type: %s%s\r\n", zReplyMimeType,
552
+ content_type_charset(zReplyMimeType));
553
+ if( fossil_strcmp(zReplyMimeType,"application/x-fossil")==0 ){
550554
cgi_combine_header_and_body();
551555
blob_compress(&cgiContent[0], &cgiContent[0]);
552556
}
553557
554558
if( is_gzippable() && iReplyStatus!=206 ){
@@ -944,10 +948,20 @@
944948
** portion is fixed but a copy is be made of zValue.
945949
*/
946950
void cgi_setenv(const char *zName, const char *zValue){
947951
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
948952
}
953
+
954
+/*
955
+** Returns true if NUL-terminated z contains any non-NUL
956
+** control characters (<0x20, 32d).
957
+*/
958
+static int contains_ctrl(const char *z){
959
+ assert(z);
960
+ for( ; *z>=0x20; ++z ){}
961
+ return 0!=*z;
962
+}
949963
950964
/*
951965
** Add a list of query parameters or cookies to the parameter set.
952966
**
953967
** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -974,12 +988,16 @@
974988
** before the NAME is ignored.
975989
**
976990
** The input string "z" is modified but no copies is made. "z"
977991
** should not be deallocated or changed again after this routine
978992
** returns or it will corrupt the parameter table.
993
+**
994
+** If bPermitCtrl is false and the decoded value of any entry in z
995
+** contains control characters (<0x20, 32d) then that key/value pair
996
+** are skipped.
979997
*/
980
-static void add_param_list(char *z, int terminator){
998
+static void add_param_list(char *z, int terminator, int bPermitCtrl){
981999
int isQP = terminator=='&';
9821000
while( *z ){
9831001
char *zName;
9841002
char *zValue;
9851003
while( fossil_isspace(*z) ){ z++; }
@@ -998,11 +1016,14 @@
9981016
}else{
9991017
if( *z ){ *z++ = 0; }
10001018
zValue = "";
10011019
}
10021020
if( zName[0] && fossil_no_strange_characters(zName+1) ){
1003
- if( fossil_islower(zName[0]) ){
1021
+ if( 0==bPermitCtrl && contains_ctrl(zValue) ){
1022
+ continue /* Reject it. An argument could be made
1023
+ ** for break instead of continue. */;
1024
+ }else if( fossil_islower(zName[0]) ){
10041025
cgi_set_parameter_nocopy(zName, zValue, isQP);
10051026
}else if( fossil_isupper(zName[0]) ){
10061027
cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
10071028
}
10081029
}
@@ -1297,11 +1318,11 @@
12971318
int rc = 0;
12981319
char * z = (char*)P("QUERY_STRING");
12991320
if( z ){
13001321
rc = 0x01;
13011322
z = fossil_strdup(z);
1302
- add_param_list(z, '&');
1323
+ add_param_list(z, '&', 0);
13031324
z = (char*)P("skin");
13041325
if( z ){
13051326
char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
13061327
rc |= 0x02;
13071328
if( !zErr && P("once")==0 ){
@@ -1457,11 +1478,11 @@
14571478
}
14581479
#endif
14591480
z = (char*)P("HTTP_COOKIE");
14601481
if( z ){
14611482
z = fossil_strdup(z);
1462
- add_param_list(z, ';');
1483
+ add_param_list(z, ';', 0);
14631484
z = (char*)cookie_value("skin",0);
14641485
if(z){
14651486
skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
14661487
}
14671488
}
@@ -1520,11 +1541,11 @@
15201541
|| fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
15211542
){
15221543
char *z = blob_str(&g.cgiIn);
15231544
cgi_trace(z);
15241545
if( g.zContentType[0]=='a' ){
1525
- add_param_list(z, '&');
1546
+ add_param_list(z, '&', 1);
15261547
}else{
15271548
process_multipart_form_data(z, len);
15281549
}
15291550
blob_init(&g.cgiIn, 0, 0);
15301551
}
@@ -1612,10 +1633,25 @@
16121633
}
16131634
}
16141635
CGIDEBUG(("no-match [%s]\n", zName));
16151636
return zDefault;
16161637
}
1638
+
1639
+/*
1640
+** Return TRUE if the specific parameter exists and is a query parameter.
1641
+** Return FALSE if the parameter is a cookie or environment variable.
1642
+*/
1643
+int cgi_is_qp(const char *zName){
1644
+ int i;
1645
+ if( zName==0 || fossil_isupper(zName[0]) ) return 0;
1646
+ for(i=0; i<nUsedQP; i++){
1647
+ if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
1648
+ return aParamQP[i].isQP;
1649
+ }
1650
+ }
1651
+ return 0;
1652
+}
16171653
16181654
/*
16191655
** Renders the "begone, spider" page and exits.
16201656
*/
16211657
static void cgi_begone_spider(const char *zName){
@@ -2006,11 +2042,11 @@
20062042
char *z;
20072043
va_start(ap, zMsg);
20082044
z = vmprintf(zMsg, ap);
20092045
va_end(ap);
20102046
cgi_set_status(400, "Bad Request");
2011
- zContentType = "text/plain";
2047
+ zReplyMimeType = "text/plain";
20122048
if( g.zReqType==0 ) g.zReqType = "WWW";
20132049
if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
20142050
const char *zServer = PD("SERVER_SOFTWARE","");
20152051
cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
20162052
}else{
@@ -2254,12 +2290,13 @@
22542290
*/
22552291
void cgi_handle_ssh_http_request(const char *zIpAddr){
22562292
static int nCycles = 0;
22572293
static char *zCmd = 0;
22582294
char *z, *zToken;
2259
- const char *zType = 0;
2260
- int i, content_length = 0;
2295
+ char *zMethod;
2296
+ int i;
2297
+ size_t n;
22612298
char zLine[2000]; /* A single line of input. */
22622299
22632300
assert( !g.httpUseSSL );
22642301
#ifdef FOSSIL_ENABLE_JSON
22652302
if( nCycles==0 ){ json_bootstrap_early(); }
@@ -2304,10 +2341,11 @@
23042341
if( zToken==0 ){
23052342
malformed_request("malformed HTTP header");
23062343
}
23072344
}
23082345
2346
+ zMethod = fossil_strdup(zToken);
23092347
if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
23102348
&& fossil_strcmp(zToken,"HEAD")!=0 ){
23112349
malformed_request("unsupported HTTP method");
23122350
}
23132351
@@ -2317,10 +2355,20 @@
23172355
}
23182356
23192357
zToken = extract_token(z, &z);
23202358
if( zToken==0 ){
23212359
malformed_request("malformed URL in HTTP header");
2360
+ }
2361
+ n = strlen(g.zRepositoryName);
2362
+ if( fossil_strncmp(g.zRepositoryName, zToken, n)==0
2363
+ && (zToken[n]=='/' || zToken[n]==0)
2364
+ && fossil_strcmp(zMethod,"GET")==0
2365
+ ){
2366
+ zToken += n;
2367
+ if( zToken && strlen(zToken)==0 ){
2368
+ malformed_request("malformed URL in HTTP header");
2369
+ }
23222370
}
23232371
if( nCycles==0 ){
23242372
cgi_setenv("REQUEST_URI", zToken);
23252373
cgi_setenv("SCRIPT_NAME", "");
23262374
}
@@ -2327,12 +2375,14 @@
23272375
23282376
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
23292377
if( zToken[i] ) zToken[i++] = 0;
23302378
if( nCycles==0 ){
23312379
cgi_setenv("PATH_INFO", zToken);
2380
+ cgi_setenv("QUERY_STRING",&zToken[i]);
23322381
}else{
23332382
cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
2383
+ cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
23342384
}
23352385
23362386
/* Get all the optional fields that follow the first line.
23372387
*/
23382388
while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2348,13 +2398,19 @@
23482398
zVal[i] = 0;
23492399
for(i=0; zFieldName[i]; i++){
23502400
zFieldName[i] = fossil_tolower(zFieldName[i]);
23512401
}
23522402
if( fossil_strcmp(zFieldName,"content-length:")==0 ){
2353
- content_length = atoi(zVal);
2403
+ if( nCycles==0 ){
2404
+ cgi_setenv("CONTENT_LENGTH", zVal);
2405
+ }else{
2406
+ cgi_replace_parameter("CONTENT_LENGTH", zVal);
2407
+ }
23542408
}else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
2355
- g.zContentType = zType = fossil_strdup(zVal);
2409
+ if( nCycles==0 ){
2410
+ cgi_setenv("CONTENT_TYPE", zVal);
2411
+ }
23562412
}else if( fossil_strcmp(zFieldName,"host:")==0 ){
23572413
if( nCycles==0 ){
23582414
cgi_setenv("HTTP_HOST", zVal);
23592415
}
23602416
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
@@ -2381,21 +2437,11 @@
23812437
}
23822438
23832439
cgi_reset_content();
23842440
cgi_destination(CGI_BODY);
23852441
2386
- if( content_length>0 && zType ){
2387
- blob_zero(&g.cgiIn);
2388
- if( fossil_strcmp(zType, "application/x-fossil")==0 ){
2389
- blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2390
- blob_uncompress(&g.cgiIn, &g.cgiIn);
2391
- }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
2392
- blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2393
- }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
2394
- blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2395
- }
2396
- }
2442
+ cgi_init();
23972443
cgi_trace(0);
23982444
nCycles++;
23992445
}
24002446
24012447
/*
24022448
--- src/cgi.c
+++ src/cgi.c
@@ -241,11 +241,11 @@
241 }
242
243 /*
244 ** Additional information used to form the HTTP reply
245 */
246 static const char *zContentType = "text/html"; /* Content type of the reply */
247 static const char *zReplyStatus = "OK"; /* Reply status description */
248 static int iReplyStatus = 200; /* Reply status code */
249 static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
250 static int rangeStart = 0; /* Start of Range: */
251 static int rangeEnd = 0; /* End of Range: plus 1 */
@@ -256,11 +256,13 @@
256 ** The reply content type defaults to "text/html". It only needs to be
257 ** changed (by calling this routine) in the exceptional case where some
258 ** other content type is being returned.
259 */
260 void cgi_set_content_type(const char *zType){
261 zContentType = fossil_strdup(zType);
 
 
262 }
263
264 /*
265 ** Erase any existing reply content. Replace is with a pNewContent.
266 **
@@ -336,14 +338,14 @@
336 if( g.fNoHttpCompress ) return 0;
337 if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
338 /* Maintenance note: this oddball structure is intended to make
339 ** adding new mimetypes to this list less of a performance hit than
340 ** doing a strcmp/glob over a growing set of compressible types. */
341 switch(zContentType ? *zContentType : 0){
342 case (int)'a':
343 if(0==fossil_strncmp("application/",zContentType,12)){
344 const char * z = &zContentType[12];
345 switch(*z){
346 case (int)'j':
347 return fossil_strcmp("javascript", z)==0
348 || fossil_strcmp("json", z)==0;
349 case (int)'w': return fossil_strcmp("wasm", z)==0;
@@ -354,14 +356,14 @@
354 return sqlite3_strglob("*xml", z)==0;
355 }
356 }
357 break;
358 case (int)'i':
359 return fossil_strcmp(zContentType, "image/svg+xml")==0
360 || fossil_strcmp(zContentType, "image/vnd.microsoft.icon")==0;
361 case (int)'t':
362 return fossil_strncmp(zContentType, "text/", 5)==0;
363 }
364 return 0;
365 }
366
367
@@ -402,10 +404,12 @@
402 }
403 #ifdef FOSSIL_ENABLE_SSL
404 return ssl_read_server(g.httpSSLConn, ptr, nmemb, 1);
405 #else
406 fossil_fatal("SSL not available");
 
 
407 #endif
408 }
409
410 /* Works like feof():
411 **
@@ -449,22 +453,22 @@
449 }
450
451 /*
452 ** Given a Content-Type value, returns a string suitable for appending
453 ** to the Content-Type header for adding (or not) the "; charset=..."
454 ** part. It returns an empty string for most types or if zContentType
455 ** is NULL.
456 **
457 ** See forum post f60dece061c364d1 for the discussions which lead to
458 ** this. Previously we always appended the charset, but WASM loaders
459 ** are pedantic and refuse to load any responses which have a
460 ** charset. Also, adding a charset is not strictly appropriate for
461 ** most types (and not required for many others which may ostensibly
462 ** benefit from one, as detailed in that forum post).
463 */
464 static const char * content_type_charset(const char *zContentType){
465 if(0==fossil_strncmp(zContentType,"text/",5)){
466 return "; charset=utf-8";
467 }
468 return "";
469 }
470
@@ -500,11 +504,11 @@
500 assert( rangeEnd==0 );
501 blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
502 }
503 if( etag_tag()[0]!=0
504 && iReplyStatus==200
505 && strcmp(zContentType,"text/html")!=0
506 ){
507 /* Do not cache HTML replies as those will have been generated and
508 ** will likely, therefore, contains a nonce and we want that nonce to
509 ** be different every time. */
510 blob_appendf(&hdr, "ETag: \"%s\"\r\n", etag_tag());
@@ -542,13 +546,13 @@
542 ** These headers are probably best added by the web server hosting fossil as
543 ** a CGI script.
544 */
545
546 if( iReplyStatus!=304 ) {
547 blob_appendf(&hdr, "Content-Type: %s%s\r\n", zContentType,
548 content_type_charset(zContentType));
549 if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
550 cgi_combine_header_and_body();
551 blob_compress(&cgiContent[0], &cgiContent[0]);
552 }
553
554 if( is_gzippable() && iReplyStatus!=206 ){
@@ -944,10 +948,20 @@
944 ** portion is fixed but a copy is be made of zValue.
945 */
946 void cgi_setenv(const char *zName, const char *zValue){
947 cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
948 }
 
 
 
 
 
 
 
 
 
 
949
950 /*
951 ** Add a list of query parameters or cookies to the parameter set.
952 **
953 ** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -974,12 +988,16 @@
974 ** before the NAME is ignored.
975 **
976 ** The input string "z" is modified but no copies is made. "z"
977 ** should not be deallocated or changed again after this routine
978 ** returns or it will corrupt the parameter table.
 
 
 
 
979 */
980 static void add_param_list(char *z, int terminator){
981 int isQP = terminator=='&';
982 while( *z ){
983 char *zName;
984 char *zValue;
985 while( fossil_isspace(*z) ){ z++; }
@@ -998,11 +1016,14 @@
998 }else{
999 if( *z ){ *z++ = 0; }
1000 zValue = "";
1001 }
1002 if( zName[0] && fossil_no_strange_characters(zName+1) ){
1003 if( fossil_islower(zName[0]) ){
 
 
 
1004 cgi_set_parameter_nocopy(zName, zValue, isQP);
1005 }else if( fossil_isupper(zName[0]) ){
1006 cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
1007 }
1008 }
@@ -1297,11 +1318,11 @@
1297 int rc = 0;
1298 char * z = (char*)P("QUERY_STRING");
1299 if( z ){
1300 rc = 0x01;
1301 z = fossil_strdup(z);
1302 add_param_list(z, '&');
1303 z = (char*)P("skin");
1304 if( z ){
1305 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1306 rc |= 0x02;
1307 if( !zErr && P("once")==0 ){
@@ -1457,11 +1478,11 @@
1457 }
1458 #endif
1459 z = (char*)P("HTTP_COOKIE");
1460 if( z ){
1461 z = fossil_strdup(z);
1462 add_param_list(z, ';');
1463 z = (char*)cookie_value("skin",0);
1464 if(z){
1465 skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
1466 }
1467 }
@@ -1520,11 +1541,11 @@
1520 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
1521 ){
1522 char *z = blob_str(&g.cgiIn);
1523 cgi_trace(z);
1524 if( g.zContentType[0]=='a' ){
1525 add_param_list(z, '&');
1526 }else{
1527 process_multipart_form_data(z, len);
1528 }
1529 blob_init(&g.cgiIn, 0, 0);
1530 }
@@ -1612,10 +1633,25 @@
1612 }
1613 }
1614 CGIDEBUG(("no-match [%s]\n", zName));
1615 return zDefault;
1616 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1617
1618 /*
1619 ** Renders the "begone, spider" page and exits.
1620 */
1621 static void cgi_begone_spider(const char *zName){
@@ -2006,11 +2042,11 @@
2006 char *z;
2007 va_start(ap, zMsg);
2008 z = vmprintf(zMsg, ap);
2009 va_end(ap);
2010 cgi_set_status(400, "Bad Request");
2011 zContentType = "text/plain";
2012 if( g.zReqType==0 ) g.zReqType = "WWW";
2013 if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
2014 const char *zServer = PD("SERVER_SOFTWARE","");
2015 cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
2016 }else{
@@ -2254,12 +2290,13 @@
2254 */
2255 void cgi_handle_ssh_http_request(const char *zIpAddr){
2256 static int nCycles = 0;
2257 static char *zCmd = 0;
2258 char *z, *zToken;
2259 const char *zType = 0;
2260 int i, content_length = 0;
 
2261 char zLine[2000]; /* A single line of input. */
2262
2263 assert( !g.httpUseSSL );
2264 #ifdef FOSSIL_ENABLE_JSON
2265 if( nCycles==0 ){ json_bootstrap_early(); }
@@ -2304,10 +2341,11 @@
2304 if( zToken==0 ){
2305 malformed_request("malformed HTTP header");
2306 }
2307 }
2308
 
2309 if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
2310 && fossil_strcmp(zToken,"HEAD")!=0 ){
2311 malformed_request("unsupported HTTP method");
2312 }
2313
@@ -2317,10 +2355,20 @@
2317 }
2318
2319 zToken = extract_token(z, &z);
2320 if( zToken==0 ){
2321 malformed_request("malformed URL in HTTP header");
 
 
 
 
 
 
 
 
 
 
2322 }
2323 if( nCycles==0 ){
2324 cgi_setenv("REQUEST_URI", zToken);
2325 cgi_setenv("SCRIPT_NAME", "");
2326 }
@@ -2327,12 +2375,14 @@
2327
2328 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2329 if( zToken[i] ) zToken[i++] = 0;
2330 if( nCycles==0 ){
2331 cgi_setenv("PATH_INFO", zToken);
 
2332 }else{
2333 cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
 
2334 }
2335
2336 /* Get all the optional fields that follow the first line.
2337 */
2338 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2348,13 +2398,19 @@
2348 zVal[i] = 0;
2349 for(i=0; zFieldName[i]; i++){
2350 zFieldName[i] = fossil_tolower(zFieldName[i]);
2351 }
2352 if( fossil_strcmp(zFieldName,"content-length:")==0 ){
2353 content_length = atoi(zVal);
 
 
 
 
2354 }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
2355 g.zContentType = zType = fossil_strdup(zVal);
 
 
2356 }else if( fossil_strcmp(zFieldName,"host:")==0 ){
2357 if( nCycles==0 ){
2358 cgi_setenv("HTTP_HOST", zVal);
2359 }
2360 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
@@ -2381,21 +2437,11 @@
2381 }
2382
2383 cgi_reset_content();
2384 cgi_destination(CGI_BODY);
2385
2386 if( content_length>0 && zType ){
2387 blob_zero(&g.cgiIn);
2388 if( fossil_strcmp(zType, "application/x-fossil")==0 ){
2389 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2390 blob_uncompress(&g.cgiIn, &g.cgiIn);
2391 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
2392 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2393 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
2394 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2395 }
2396 }
2397 cgi_trace(0);
2398 nCycles++;
2399 }
2400
2401 /*
2402
--- src/cgi.c
+++ src/cgi.c
@@ -241,11 +241,11 @@
241 }
242
243 /*
244 ** Additional information used to form the HTTP reply
245 */
246 static const char *zReplyMimeType = "text/html"; /* Content type of the reply */
247 static const char *zReplyStatus = "OK"; /* Reply status description */
248 static int iReplyStatus = 200; /* Reply status code */
249 static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
250 static int rangeStart = 0; /* Start of Range: */
251 static int rangeEnd = 0; /* End of Range: plus 1 */
@@ -256,11 +256,13 @@
256 ** The reply content type defaults to "text/html". It only needs to be
257 ** changed (by calling this routine) in the exceptional case where some
258 ** other content type is being returned.
259 */
260 void cgi_set_content_type(const char *zType){
261 int i;
262 for(i=0; zType[i]>='+' && zType[i]<='z'; i++){}
263 zReplyMimeType = fossil_strndup(zType, i);
264 }
265
266 /*
267 ** Erase any existing reply content. Replace is with a pNewContent.
268 **
@@ -336,14 +338,14 @@
338 if( g.fNoHttpCompress ) return 0;
339 if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
340 /* Maintenance note: this oddball structure is intended to make
341 ** adding new mimetypes to this list less of a performance hit than
342 ** doing a strcmp/glob over a growing set of compressible types. */
343 switch(zReplyMimeType ? *zReplyMimeType : 0){
344 case (int)'a':
345 if(0==fossil_strncmp("application/",zReplyMimeType,12)){
346 const char * z = &zReplyMimeType[12];
347 switch(*z){
348 case (int)'j':
349 return fossil_strcmp("javascript", z)==0
350 || fossil_strcmp("json", z)==0;
351 case (int)'w': return fossil_strcmp("wasm", z)==0;
@@ -354,14 +356,14 @@
356 return sqlite3_strglob("*xml", z)==0;
357 }
358 }
359 break;
360 case (int)'i':
361 return fossil_strcmp(zReplyMimeType, "image/svg+xml")==0
362 || fossil_strcmp(zReplyMimeType, "image/vnd.microsoft.icon")==0;
363 case (int)'t':
364 return fossil_strncmp(zReplyMimeType, "text/", 5)==0;
365 }
366 return 0;
367 }
368
369
@@ -402,10 +404,12 @@
404 }
405 #ifdef FOSSIL_ENABLE_SSL
406 return ssl_read_server(g.httpSSLConn, ptr, nmemb, 1);
407 #else
408 fossil_fatal("SSL not available");
409 /* NOT REACHED */
410 return 0;
411 #endif
412 }
413
414 /* Works like feof():
415 **
@@ -449,22 +453,22 @@
453 }
454
455 /*
456 ** Given a Content-Type value, returns a string suitable for appending
457 ** to the Content-Type header for adding (or not) the "; charset=..."
458 ** part. It returns an empty string for most types or if zReplyMimeType
459 ** is NULL.
460 **
461 ** See forum post f60dece061c364d1 for the discussions which lead to
462 ** this. Previously we always appended the charset, but WASM loaders
463 ** are pedantic and refuse to load any responses which have a
464 ** charset. Also, adding a charset is not strictly appropriate for
465 ** most types (and not required for many others which may ostensibly
466 ** benefit from one, as detailed in that forum post).
467 */
468 static const char * content_type_charset(const char *zReplyMimeType){
469 if(0==fossil_strncmp(zReplyMimeType,"text/",5)){
470 return "; charset=utf-8";
471 }
472 return "";
473 }
474
@@ -500,11 +504,11 @@
504 assert( rangeEnd==0 );
505 blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
506 }
507 if( etag_tag()[0]!=0
508 && iReplyStatus==200
509 && strcmp(zReplyMimeType,"text/html")!=0
510 ){
511 /* Do not cache HTML replies as those will have been generated and
512 ** will likely, therefore, contains a nonce and we want that nonce to
513 ** be different every time. */
514 blob_appendf(&hdr, "ETag: \"%s\"\r\n", etag_tag());
@@ -542,13 +546,13 @@
546 ** These headers are probably best added by the web server hosting fossil as
547 ** a CGI script.
548 */
549
550 if( iReplyStatus!=304 ) {
551 blob_appendf(&hdr, "Content-Type: %s%s\r\n", zReplyMimeType,
552 content_type_charset(zReplyMimeType));
553 if( fossil_strcmp(zReplyMimeType,"application/x-fossil")==0 ){
554 cgi_combine_header_and_body();
555 blob_compress(&cgiContent[0], &cgiContent[0]);
556 }
557
558 if( is_gzippable() && iReplyStatus!=206 ){
@@ -944,10 +948,20 @@
948 ** portion is fixed but a copy is be made of zValue.
949 */
950 void cgi_setenv(const char *zName, const char *zValue){
951 cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
952 }
953
954 /*
955 ** Returns true if NUL-terminated z contains any non-NUL
956 ** control characters (<0x20, 32d).
957 */
958 static int contains_ctrl(const char *z){
959 assert(z);
960 for( ; *z>=0x20; ++z ){}
961 return 0!=*z;
962 }
963
964 /*
965 ** Add a list of query parameters or cookies to the parameter set.
966 **
967 ** Each parameter is of the form NAME=VALUE. Both the NAME and the
@@ -974,12 +988,16 @@
988 ** before the NAME is ignored.
989 **
990 ** The input string "z" is modified but no copies is made. "z"
991 ** should not be deallocated or changed again after this routine
992 ** returns or it will corrupt the parameter table.
993 **
994 ** If bPermitCtrl is false and the decoded value of any entry in z
995 ** contains control characters (<0x20, 32d) then that key/value pair
996 ** are skipped.
997 */
998 static void add_param_list(char *z, int terminator, int bPermitCtrl){
999 int isQP = terminator=='&';
1000 while( *z ){
1001 char *zName;
1002 char *zValue;
1003 while( fossil_isspace(*z) ){ z++; }
@@ -998,11 +1016,14 @@
1016 }else{
1017 if( *z ){ *z++ = 0; }
1018 zValue = "";
1019 }
1020 if( zName[0] && fossil_no_strange_characters(zName+1) ){
1021 if( 0==bPermitCtrl && contains_ctrl(zValue) ){
1022 continue /* Reject it. An argument could be made
1023 ** for break instead of continue. */;
1024 }else if( fossil_islower(zName[0]) ){
1025 cgi_set_parameter_nocopy(zName, zValue, isQP);
1026 }else if( fossil_isupper(zName[0]) ){
1027 cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
1028 }
1029 }
@@ -1297,11 +1318,11 @@
1318 int rc = 0;
1319 char * z = (char*)P("QUERY_STRING");
1320 if( z ){
1321 rc = 0x01;
1322 z = fossil_strdup(z);
1323 add_param_list(z, '&', 0);
1324 z = (char*)P("skin");
1325 if( z ){
1326 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1327 rc |= 0x02;
1328 if( !zErr && P("once")==0 ){
@@ -1457,11 +1478,11 @@
1478 }
1479 #endif
1480 z = (char*)P("HTTP_COOKIE");
1481 if( z ){
1482 z = fossil_strdup(z);
1483 add_param_list(z, ';', 0);
1484 z = (char*)cookie_value("skin",0);
1485 if(z){
1486 skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
1487 }
1488 }
@@ -1520,11 +1541,11 @@
1541 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
1542 ){
1543 char *z = blob_str(&g.cgiIn);
1544 cgi_trace(z);
1545 if( g.zContentType[0]=='a' ){
1546 add_param_list(z, '&', 1);
1547 }else{
1548 process_multipart_form_data(z, len);
1549 }
1550 blob_init(&g.cgiIn, 0, 0);
1551 }
@@ -1612,10 +1633,25 @@
1633 }
1634 }
1635 CGIDEBUG(("no-match [%s]\n", zName));
1636 return zDefault;
1637 }
1638
1639 /*
1640 ** Return TRUE if the specific parameter exists and is a query parameter.
1641 ** Return FALSE if the parameter is a cookie or environment variable.
1642 */
1643 int cgi_is_qp(const char *zName){
1644 int i;
1645 if( zName==0 || fossil_isupper(zName[0]) ) return 0;
1646 for(i=0; i<nUsedQP; i++){
1647 if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
1648 return aParamQP[i].isQP;
1649 }
1650 }
1651 return 0;
1652 }
1653
1654 /*
1655 ** Renders the "begone, spider" page and exits.
1656 */
1657 static void cgi_begone_spider(const char *zName){
@@ -2006,11 +2042,11 @@
2042 char *z;
2043 va_start(ap, zMsg);
2044 z = vmprintf(zMsg, ap);
2045 va_end(ap);
2046 cgi_set_status(400, "Bad Request");
2047 zReplyMimeType = "text/plain";
2048 if( g.zReqType==0 ) g.zReqType = "WWW";
2049 if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
2050 const char *zServer = PD("SERVER_SOFTWARE","");
2051 cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
2052 }else{
@@ -2254,12 +2290,13 @@
2290 */
2291 void cgi_handle_ssh_http_request(const char *zIpAddr){
2292 static int nCycles = 0;
2293 static char *zCmd = 0;
2294 char *z, *zToken;
2295 char *zMethod;
2296 int i;
2297 size_t n;
2298 char zLine[2000]; /* A single line of input. */
2299
2300 assert( !g.httpUseSSL );
2301 #ifdef FOSSIL_ENABLE_JSON
2302 if( nCycles==0 ){ json_bootstrap_early(); }
@@ -2304,10 +2341,11 @@
2341 if( zToken==0 ){
2342 malformed_request("malformed HTTP header");
2343 }
2344 }
2345
2346 zMethod = fossil_strdup(zToken);
2347 if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
2348 && fossil_strcmp(zToken,"HEAD")!=0 ){
2349 malformed_request("unsupported HTTP method");
2350 }
2351
@@ -2317,10 +2355,20 @@
2355 }
2356
2357 zToken = extract_token(z, &z);
2358 if( zToken==0 ){
2359 malformed_request("malformed URL in HTTP header");
2360 }
2361 n = strlen(g.zRepositoryName);
2362 if( fossil_strncmp(g.zRepositoryName, zToken, n)==0
2363 && (zToken[n]=='/' || zToken[n]==0)
2364 && fossil_strcmp(zMethod,"GET")==0
2365 ){
2366 zToken += n;
2367 if( zToken && strlen(zToken)==0 ){
2368 malformed_request("malformed URL in HTTP header");
2369 }
2370 }
2371 if( nCycles==0 ){
2372 cgi_setenv("REQUEST_URI", zToken);
2373 cgi_setenv("SCRIPT_NAME", "");
2374 }
@@ -2327,12 +2375,14 @@
2375
2376 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2377 if( zToken[i] ) zToken[i++] = 0;
2378 if( nCycles==0 ){
2379 cgi_setenv("PATH_INFO", zToken);
2380 cgi_setenv("QUERY_STRING",&zToken[i]);
2381 }else{
2382 cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
2383 cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
2384 }
2385
2386 /* Get all the optional fields that follow the first line.
2387 */
2388 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2348,13 +2398,19 @@
2398 zVal[i] = 0;
2399 for(i=0; zFieldName[i]; i++){
2400 zFieldName[i] = fossil_tolower(zFieldName[i]);
2401 }
2402 if( fossil_strcmp(zFieldName,"content-length:")==0 ){
2403 if( nCycles==0 ){
2404 cgi_setenv("CONTENT_LENGTH", zVal);
2405 }else{
2406 cgi_replace_parameter("CONTENT_LENGTH", zVal);
2407 }
2408 }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
2409 if( nCycles==0 ){
2410 cgi_setenv("CONTENT_TYPE", zVal);
2411 }
2412 }else if( fossil_strcmp(zFieldName,"host:")==0 ){
2413 if( nCycles==0 ){
2414 cgi_setenv("HTTP_HOST", zVal);
2415 }
2416 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
@@ -2381,21 +2437,11 @@
2437 }
2438
2439 cgi_reset_content();
2440 cgi_destination(CGI_BODY);
2441
2442 cgi_init();
 
 
 
 
 
 
 
 
 
 
2443 cgi_trace(0);
2444 nCycles++;
2445 }
2446
2447 /*
2448
+18
--- src/chat.c
+++ src/chat.c
@@ -389,10 +389,25 @@
389389
}else{
390390
CX("}");
391391
}
392392
fossil_free(zTime);
393393
}
394
+
395
+/*
396
+** Like chat_emit_permissions_error() but emits a single
397
+** /chat-message-format JSON object about a CSRF violation.
398
+*/
399
+static void chat_emit_csrf_error(void){
400
+ char * zTime = cgi_iso8601_datestamp();
401
+ cgi_set_content_type("application/json");
402
+ CX("{");
403
+ CX("\"isError\": true, \"xfrom\": null,");
404
+ CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime);
405
+ CX("\"xmsg\": \"CSRF validation failure.\"");
406
+ CX("}");
407
+ fossil_free(zTime);
408
+}
394409
395410
/*
396411
** WEBPAGE: chat-send hidden loadavg-exempt
397412
**
398413
** This page receives (via XHR) a new chat-message and/or a new file
@@ -421,10 +436,13 @@
421436
const char *zMsg;
422437
const char *zUserName;
423438
login_check_credentials();
424439
if( 0==g.perm.Chat ) {
425440
chat_emit_permissions_error(0);
441
+ return;
442
+ }else if( 0==cgi_csrf_safe(1) ){
443
+ chat_emit_csrf_error();
426444
return;
427445
}
428446
zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody";
429447
nByte = atoi(PD("file:bytes","0"));
430448
zMsg = PD("msg","");
431449
--- src/chat.c
+++ src/chat.c
@@ -389,10 +389,25 @@
389 }else{
390 CX("}");
391 }
392 fossil_free(zTime);
393 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
395 /*
396 ** WEBPAGE: chat-send hidden loadavg-exempt
397 **
398 ** This page receives (via XHR) a new chat-message and/or a new file
@@ -421,10 +436,13 @@
421 const char *zMsg;
422 const char *zUserName;
423 login_check_credentials();
424 if( 0==g.perm.Chat ) {
425 chat_emit_permissions_error(0);
 
 
 
426 return;
427 }
428 zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody";
429 nByte = atoi(PD("file:bytes","0"));
430 zMsg = PD("msg","");
431
--- src/chat.c
+++ src/chat.c
@@ -389,10 +389,25 @@
389 }else{
390 CX("}");
391 }
392 fossil_free(zTime);
393 }
394
395 /*
396 ** Like chat_emit_permissions_error() but emits a single
397 ** /chat-message-format JSON object about a CSRF violation.
398 */
399 static void chat_emit_csrf_error(void){
400 char * zTime = cgi_iso8601_datestamp();
401 cgi_set_content_type("application/json");
402 CX("{");
403 CX("\"isError\": true, \"xfrom\": null,");
404 CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime);
405 CX("\"xmsg\": \"CSRF validation failure.\"");
406 CX("}");
407 fossil_free(zTime);
408 }
409
410 /*
411 ** WEBPAGE: chat-send hidden loadavg-exempt
412 **
413 ** This page receives (via XHR) a new chat-message and/or a new file
@@ -421,10 +436,13 @@
436 const char *zMsg;
437 const char *zUserName;
438 login_check_credentials();
439 if( 0==g.perm.Chat ) {
440 chat_emit_permissions_error(0);
441 return;
442 }else if( 0==cgi_csrf_safe(1) ){
443 chat_emit_csrf_error();
444 return;
445 }
446 zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody";
447 nByte = atoi(PD("file:bytes","0"));
448 zMsg = PD("msg","");
449
+16 -6
--- src/checkin.c
+++ src/checkin.c
@@ -718,10 +718,11 @@
718718
*/
719719
static void ls_cmd_rev(
720720
const char *zRev, /* Revision string given */
721721
int verboseFlag, /* Verbose flag given */
722722
int showAge, /* Age flag given */
723
+ int showHash, /* Show hash flag given */
723724
int timeOrder, /* Order by time flag given */
724725
int treeFmt /* Show output in the tree format */
725726
){
726727
Stmt q;
727728
char *zOrderBy = "pathname COLLATE nocase";
@@ -763,11 +764,11 @@
763764
}
764765
765766
compute_fileage(rid,0);
766767
db_prepare(&q,
767768
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
768
- " blob.size\n"
769
+ " blob.size, fileage.uuid\n"
769770
" FROM fileage, blob\n"
770771
" WHERE blob.rid=fileage.fid %s\n"
771772
" ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
772773
);
773774
blob_reset(&where);
@@ -778,11 +779,16 @@
778779
const char *zFile = db_column_text(&q,1);
779780
int size = db_column_int(&q,2);
780781
if( treeFmt ){
781782
blob_appendf(&out, "%s\n", zFile);
782783
}else if( verboseFlag ){
783
- fossil_print("%s %7d %s\n", zTime, size, zFile);
784
+ if( showHash ){
785
+ const char *zUuid = db_column_text(&q,3);
786
+ fossil_print("%s %7d [%S] %s\n", zTime, size, zUuid, zFile);
787
+ }else{
788
+ fossil_print("%s %7d %s\n", zTime, size, zFile);
789
+ }
784790
}else if( showAge ){
785791
fossil_print("%s %s\n", zTime, zFile);
786792
}else{
787793
fossil_print("%s\n", zFile);
788794
}
@@ -811,18 +817,20 @@
811817
** The --age option displays file commit times. Like -r, --age has the
812818
** side effect of making -t sort by commit time, not modification time.
813819
**
814820
** The -v option provides extra information about each file. Without -r,
815821
** -v displays the change status, in the manner of the changes command.
816
-** With -r, -v shows the commit time and size of the checked-in files.
822
+** With -r, -v shows the commit time and size of the checked-in files; in
823
+** this combination, it additionally shows file hashes with -h.
817824
**
818825
** The -t option changes the sort order. Without -t, files are sorted by
819826
** path and name (case insensitive sort if -r). If neither --age nor -r
820827
** are used, -t sorts by modification time, otherwise by commit time.
821828
**
822829
** Options:
823830
** --age Show when each file was committed
831
+** -h With -v and -r, show file hashes
824832
** --hash With -v, verify file status using hashing
825833
** rather than relying on file sizes and mtimes
826834
** -r VERSION The specific check-in to list
827835
** -R|--repository REPO Extract info from repository REPO
828836
** -t Sort output in time order
@@ -840,10 +848,11 @@
840848
int timeOrder;
841849
char *zOrderBy = "pathname";
842850
Blob where;
843851
int i;
844852
int useHash = 0;
853
+ int showHash = 0;
845854
const char *zName;
846855
const char *zRev;
847856
848857
verboseFlag = find_option("verbose","v", 0)!=0;
849858
if( !verboseFlag ){
@@ -852,20 +861,21 @@
852861
showAge = find_option("age",0,0)!=0;
853862
zRev = find_option("r","r",1);
854863
timeOrder = find_option("t","t",0)!=0;
855864
if( verboseFlag ){
856865
useHash = find_option("hash",0,0)!=0;
866
+ showHash = find_option("h","h",0)!=0;
857867
}
858868
treeFmt = find_option("tree",0,0)!=0;
859869
if( treeFmt ){
860870
if( zRev==0 ) zRev = "current";
861871
}
862872
863873
if( zRev!=0 ){
864874
db_find_and_open_repository(0, 0);
865875
verify_all_options();
866
- ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder,treeFmt);
876
+ ls_cmd_rev(zRev,verboseFlag,showAge,showHash,timeOrder,treeFmt);
867877
return;
868878
}else if( find_option("R",0,1)!=0 ){
869879
fossil_fatal("the -r is required in addition to -R");
870880
}
871881
@@ -984,11 +994,11 @@
984994
985995
zRev = find_option("r","r",1);
986996
if( zRev==0 ) zRev = "current";
987997
db_find_and_open_repository(0, 0);
988998
verify_all_options();
989
- ls_cmd_rev(zRev,0,0,0,1);
999
+ ls_cmd_rev(zRev,0,0,0,0,1);
9901000
}
9911001
9921002
/*
9931003
** COMMAND: extras
9941004
**
@@ -1502,11 +1512,11 @@
15021512
"# * All other text will be displayed as written\n", -1);
15031513
}else{
15041514
blob_append(&prompt,
15051515
"# * Hyperlinks: [target] or [target|display-text]\n"
15061516
"# * Blank lines cause a paragraph break\n"
1507
- "# * Other text rendered as if it where HTML\n", -1
1517
+ "# * Other text rendered as if it were HTML\n", -1
15081518
);
15091519
}
15101520
blob_append(&prompt, "#\n", 2);
15111521
15121522
if( dryRunFlag ){
15131523
--- src/checkin.c
+++ src/checkin.c
@@ -718,10 +718,11 @@
718 */
719 static void ls_cmd_rev(
720 const char *zRev, /* Revision string given */
721 int verboseFlag, /* Verbose flag given */
722 int showAge, /* Age flag given */
 
723 int timeOrder, /* Order by time flag given */
724 int treeFmt /* Show output in the tree format */
725 ){
726 Stmt q;
727 char *zOrderBy = "pathname COLLATE nocase";
@@ -763,11 +764,11 @@
763 }
764
765 compute_fileage(rid,0);
766 db_prepare(&q,
767 "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
768 " blob.size\n"
769 " FROM fileage, blob\n"
770 " WHERE blob.rid=fileage.fid %s\n"
771 " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
772 );
773 blob_reset(&where);
@@ -778,11 +779,16 @@
778 const char *zFile = db_column_text(&q,1);
779 int size = db_column_int(&q,2);
780 if( treeFmt ){
781 blob_appendf(&out, "%s\n", zFile);
782 }else if( verboseFlag ){
783 fossil_print("%s %7d %s\n", zTime, size, zFile);
 
 
 
 
 
784 }else if( showAge ){
785 fossil_print("%s %s\n", zTime, zFile);
786 }else{
787 fossil_print("%s\n", zFile);
788 }
@@ -811,18 +817,20 @@
811 ** The --age option displays file commit times. Like -r, --age has the
812 ** side effect of making -t sort by commit time, not modification time.
813 **
814 ** The -v option provides extra information about each file. Without -r,
815 ** -v displays the change status, in the manner of the changes command.
816 ** With -r, -v shows the commit time and size of the checked-in files.
 
817 **
818 ** The -t option changes the sort order. Without -t, files are sorted by
819 ** path and name (case insensitive sort if -r). If neither --age nor -r
820 ** are used, -t sorts by modification time, otherwise by commit time.
821 **
822 ** Options:
823 ** --age Show when each file was committed
 
824 ** --hash With -v, verify file status using hashing
825 ** rather than relying on file sizes and mtimes
826 ** -r VERSION The specific check-in to list
827 ** -R|--repository REPO Extract info from repository REPO
828 ** -t Sort output in time order
@@ -840,10 +848,11 @@
840 int timeOrder;
841 char *zOrderBy = "pathname";
842 Blob where;
843 int i;
844 int useHash = 0;
 
845 const char *zName;
846 const char *zRev;
847
848 verboseFlag = find_option("verbose","v", 0)!=0;
849 if( !verboseFlag ){
@@ -852,20 +861,21 @@
852 showAge = find_option("age",0,0)!=0;
853 zRev = find_option("r","r",1);
854 timeOrder = find_option("t","t",0)!=0;
855 if( verboseFlag ){
856 useHash = find_option("hash",0,0)!=0;
 
857 }
858 treeFmt = find_option("tree",0,0)!=0;
859 if( treeFmt ){
860 if( zRev==0 ) zRev = "current";
861 }
862
863 if( zRev!=0 ){
864 db_find_and_open_repository(0, 0);
865 verify_all_options();
866 ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder,treeFmt);
867 return;
868 }else if( find_option("R",0,1)!=0 ){
869 fossil_fatal("the -r is required in addition to -R");
870 }
871
@@ -984,11 +994,11 @@
984
985 zRev = find_option("r","r",1);
986 if( zRev==0 ) zRev = "current";
987 db_find_and_open_repository(0, 0);
988 verify_all_options();
989 ls_cmd_rev(zRev,0,0,0,1);
990 }
991
992 /*
993 ** COMMAND: extras
994 **
@@ -1502,11 +1512,11 @@
1502 "# * All other text will be displayed as written\n", -1);
1503 }else{
1504 blob_append(&prompt,
1505 "# * Hyperlinks: [target] or [target|display-text]\n"
1506 "# * Blank lines cause a paragraph break\n"
1507 "# * Other text rendered as if it where HTML\n", -1
1508 );
1509 }
1510 blob_append(&prompt, "#\n", 2);
1511
1512 if( dryRunFlag ){
1513
--- src/checkin.c
+++ src/checkin.c
@@ -718,10 +718,11 @@
718 */
719 static void ls_cmd_rev(
720 const char *zRev, /* Revision string given */
721 int verboseFlag, /* Verbose flag given */
722 int showAge, /* Age flag given */
723 int showHash, /* Show hash flag given */
724 int timeOrder, /* Order by time flag given */
725 int treeFmt /* Show output in the tree format */
726 ){
727 Stmt q;
728 char *zOrderBy = "pathname COLLATE nocase";
@@ -763,11 +764,11 @@
764 }
765
766 compute_fileage(rid,0);
767 db_prepare(&q,
768 "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
769 " blob.size, fileage.uuid\n"
770 " FROM fileage, blob\n"
771 " WHERE blob.rid=fileage.fid %s\n"
772 " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
773 );
774 blob_reset(&where);
@@ -778,11 +779,16 @@
779 const char *zFile = db_column_text(&q,1);
780 int size = db_column_int(&q,2);
781 if( treeFmt ){
782 blob_appendf(&out, "%s\n", zFile);
783 }else if( verboseFlag ){
784 if( showHash ){
785 const char *zUuid = db_column_text(&q,3);
786 fossil_print("%s %7d [%S] %s\n", zTime, size, zUuid, zFile);
787 }else{
788 fossil_print("%s %7d %s\n", zTime, size, zFile);
789 }
790 }else if( showAge ){
791 fossil_print("%s %s\n", zTime, zFile);
792 }else{
793 fossil_print("%s\n", zFile);
794 }
@@ -811,18 +817,20 @@
817 ** The --age option displays file commit times. Like -r, --age has the
818 ** side effect of making -t sort by commit time, not modification time.
819 **
820 ** The -v option provides extra information about each file. Without -r,
821 ** -v displays the change status, in the manner of the changes command.
822 ** With -r, -v shows the commit time and size of the checked-in files; in
823 ** this combination, it additionally shows file hashes with -h.
824 **
825 ** The -t option changes the sort order. Without -t, files are sorted by
826 ** path and name (case insensitive sort if -r). If neither --age nor -r
827 ** are used, -t sorts by modification time, otherwise by commit time.
828 **
829 ** Options:
830 ** --age Show when each file was committed
831 ** -h With -v and -r, show file hashes
832 ** --hash With -v, verify file status using hashing
833 ** rather than relying on file sizes and mtimes
834 ** -r VERSION The specific check-in to list
835 ** -R|--repository REPO Extract info from repository REPO
836 ** -t Sort output in time order
@@ -840,10 +848,11 @@
848 int timeOrder;
849 char *zOrderBy = "pathname";
850 Blob where;
851 int i;
852 int useHash = 0;
853 int showHash = 0;
854 const char *zName;
855 const char *zRev;
856
857 verboseFlag = find_option("verbose","v", 0)!=0;
858 if( !verboseFlag ){
@@ -852,20 +861,21 @@
861 showAge = find_option("age",0,0)!=0;
862 zRev = find_option("r","r",1);
863 timeOrder = find_option("t","t",0)!=0;
864 if( verboseFlag ){
865 useHash = find_option("hash",0,0)!=0;
866 showHash = find_option("h","h",0)!=0;
867 }
868 treeFmt = find_option("tree",0,0)!=0;
869 if( treeFmt ){
870 if( zRev==0 ) zRev = "current";
871 }
872
873 if( zRev!=0 ){
874 db_find_and_open_repository(0, 0);
875 verify_all_options();
876 ls_cmd_rev(zRev,verboseFlag,showAge,showHash,timeOrder,treeFmt);
877 return;
878 }else if( find_option("R",0,1)!=0 ){
879 fossil_fatal("the -r is required in addition to -R");
880 }
881
@@ -984,11 +994,11 @@
994
995 zRev = find_option("r","r",1);
996 if( zRev==0 ) zRev = "current";
997 db_find_and_open_repository(0, 0);
998 verify_all_options();
999 ls_cmd_rev(zRev,0,0,0,0,1);
1000 }
1001
1002 /*
1003 ** COMMAND: extras
1004 **
@@ -1502,11 +1512,11 @@
1512 "# * All other text will be displayed as written\n", -1);
1513 }else{
1514 blob_append(&prompt,
1515 "# * Hyperlinks: [target] or [target|display-text]\n"
1516 "# * Blank lines cause a paragraph break\n"
1517 "# * Other text rendered as if it were HTML\n", -1
1518 );
1519 }
1520 blob_append(&prompt, "#\n", 2);
1521
1522 if( dryRunFlag ){
1523
--- src/checkout.c
+++ src/checkout.c
@@ -19,10 +19,11 @@
1919
** from the local repository.
2020
*/
2121
#include "config.h"
2222
#include "checkout.h"
2323
#include <assert.h>
24
+#include <zlib.h>
2425
2526
/*
2627
** Check to see if there is an existing check-out that has been
2728
** modified. Return values:
2829
**
@@ -429,5 +430,229 @@
429430
}
430431
unlink_local_database(1);
431432
db_close(1);
432433
unlink_local_database(0);
433434
}
435
+
436
+
437
+/*
438
+** COMMAND: get
439
+**
440
+** Usage: %fossil get URL ?VERSION? ?OPTIONS?
441
+**
442
+** Download a single check-in from a remote repository named URL and
443
+** unpack all of the files locally. The check-in is identified by VERSION.
444
+**
445
+** URL can be a traditional URL like one of:
446
+**
447
+** * https://domain.com/project
448
+** * ssh://my-server/project.fossil
449
+** * file:/home/user/Fossils/project.fossil
450
+**
451
+** Or URL can be just the name of a local repository without the "file:"
452
+** prefix.
453
+**
454
+** This command works by downloading an SQL archive of the requested
455
+** check-in and then extracting all the files from the archive.
456
+**
457
+** Options:
458
+** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ."
459
+** to extract into the local directory.
460
+**
461
+** -f|--force Overwrite existing files
462
+**
463
+** --list List all the files that would have been checked
464
+** out but do not actually write anything to the
465
+** filesystem.
466
+**
467
+** --sqlar ARCHIVE Store the check-out in an SQL-archive rather
468
+** than unpacking them into separate files.
469
+**
470
+** -v|--verbose Show all files as they are extracted
471
+*/
472
+void get_cmd(void){
473
+ int forceFlag = find_option("force","f",0)!=0;
474
+ int bVerbose = find_option("verbose","v",0)!=0;
475
+ int bQuiet = find_option("quiet","q",0)!=0;
476
+ int bDebug = find_option("debug",0,0)!=0;
477
+ int bList = find_option("list",0,0)!=0;
478
+ const char *zSqlArchive = find_option("sqlar",0,1);
479
+ const char *z;
480
+ char *zDest = 0; /* Where to store results */
481
+ char *zSql; /* SQL used to query the results */
482
+ const char *zUrl; /* Url to get */
483
+ const char *zVers; /* Version name to get */
484
+ unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
485
+ Blob in, out; /* I/O for the HTTP request */
486
+ Blob file; /* A file to extract */
487
+ sqlite3 *db; /* Database containing downloaded sqlar */
488
+ sqlite3_stmt *pStmt; /* Statement for querying the database */
489
+ int rc; /* Result of subroutine calls */
490
+ int nFile = 0; /* Number of files written */
491
+ int nDir = 0; /* Number of directories written */
492
+ i64 nByte = 0; /* Number of bytes written */
493
+
494
+ z = find_option("dest",0,1);
495
+ if( z ) zDest = fossil_strdup(z);
496
+ verify_all_options();
497
+ if( g.argc<3 || g.argc>4 ){
498
+ usage("URL ?VERSION? ?OPTIONS?");
499
+ }
500
+ zUrl = g.argv[2];
501
+ zVers = g.argc==4 ? g.argv[3] : "trunk";
502
+
503
+ /* Parse the URL of the repository */
504
+ url_parse(zUrl, 0);
505
+
506
+ /* Construct an appropriate name for the destination directory */
507
+ if( zDest==0 ){
508
+ int i;
509
+ const char *zTail;
510
+ const char *zDot;
511
+ int n;
512
+ if( g.url.isFile ){
513
+ zTail = file_tail(g.url.name);
514
+ }else{
515
+ zTail = file_tail(g.url.path);
516
+ }
517
+ zDot = strchr(zTail,'.');
518
+ if( zDot==0 ) zDot = zTail+strlen(zTail);
519
+ n = (int)(zDot - zTail);
520
+ zDest = mprintf("%.*s-%s", n, zTail, zVers);
521
+ for(i=0; zDest[i]; i++){
522
+ char c = zDest[i];
523
+ if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
524
+ zDest[i] = '-';
525
+ }
526
+ }
527
+ }
528
+ if( bDebug ){
529
+ fossil_print("dest = %s\n", zDest);
530
+ }
531
+
532
+ /* Error checking */
533
+ if( zDest!=file_tail(zDest) ){
534
+ fossil_fatal("--dest must be a simple directory name, not a path");
535
+ }
536
+ if( zVers!=file_tail(zVers) ){
537
+ fossil_fatal("The \"fossil get\" command does not currently work with"
538
+ " version names that contain \"/\". This will be fixed in"
539
+ " a future release.");
540
+ }
541
+ /* To relax the restrictions above, change the subpath URL formula below
542
+ ** to use query parameters. Ex: /sqlar?r=%t&name=%t */
543
+
544
+ if( !forceFlag ){
545
+ if( zSqlArchive ){
546
+ if( file_isdir(zSqlArchive, ExtFILE)>0 ){
547
+ fossil_fatal("file already exists: \"%s\"", zSqlArchive);
548
+ }
549
+ }else if( file_isdir(zDest, ExtFILE)>0 ){
550
+ if( fossil_strcmp(zDest,".")==0 ){
551
+ if( file_directory_size(zDest,0,1) ){
552
+ fossil_fatal("current directory is not empty");
553
+ }
554
+ }else{
555
+ fossil_fatal("\"%s\" already exists", zDest);
556
+ }
557
+ }
558
+ }
559
+
560
+ /* Construct a subpath on the URL if necessary */
561
+ if( g.url.isFile ){
562
+ g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
563
+ }else{
564
+ g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
565
+ }
566
+
567
+ if( bDebug ){
568
+ urlparse_print(0);
569
+ }
570
+
571
+ /* Fetch the ZIP archive for the requested check-in */
572
+ blob_init(&in, 0, 0);
573
+ blob_init(&out, 0, 0);
574
+ if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
575
+ if( bQuiet ) mHttpFlags |= HTTP_QUIET;
576
+ rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
577
+ if( rc
578
+ || out.nUsed<512
579
+ || (out.nUsed%512)!=0
580
+ || memcmp(out.aData,"SQLite format 3",16)!=0
581
+ ){
582
+ fossil_fatal("Server did not return the requested check-in.");
583
+ }
584
+
585
+ if( zSqlArchive ){
586
+ blob_write_to_file(&out, zSqlArchive);
587
+ if( bVerbose ) fossil_print("%s\n", zSqlArchive);
588
+ return;
589
+ }
590
+
591
+ rc = sqlite3_open(":memory:", &db);
592
+ if( rc==SQLITE_OK ){
593
+ int sz = blob_size(&out);
594
+ rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
595
+ SQLITE_DESERIALIZE_READONLY);
596
+ }
597
+ if( rc!=SQLITE_OK ){
598
+ fossil_fatal("Cannot create an in-memory database: %s",
599
+ sqlite3_errmsg(db));
600
+ }
601
+ zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
602
+ " WHERE name GLOB '%q*'", zDest);
603
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
604
+ fossil_free(zSql);
605
+ if( rc!=0 ){
606
+ fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
607
+ }
608
+ blob_init(&file, 0, 0);
609
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
610
+ const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
611
+ int mode = sqlite3_column_int(pStmt, 1);
612
+ int sz = sqlite3_column_int(pStmt, 2);
613
+ if( bList ){
614
+ fossil_print("%s\n", zFilename);
615
+ }else if( mode & 0x4000 ){
616
+ /* A directory name */
617
+ nDir++;
618
+ file_mkdir(zFilename, ExtFILE, 1);
619
+ }else{
620
+ /* A file */
621
+ unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
622
+ unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
623
+ unsigned long int nOut2 = (unsigned long int)sz;
624
+ nFile++;
625
+ nByte += sz;
626
+ blob_resize(&file, sz);
627
+ if( nIn<sz ){
628
+ rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
629
+ inBuf, nIn);
630
+ if( rc!=Z_OK ){
631
+ fossil_fatal("Failed to uncompress file %s", zFilename);
632
+ }
633
+ }else{
634
+ memcpy(blob_buffer(&file), inBuf, sz);
635
+ }
636
+ blob_write_to_file(&file, zFilename);
637
+ if( mode & 0x40 ){
638
+ file_setexe(zFilename, 1);
639
+ }
640
+ blob_zero(&file);
641
+ if( bVerbose ){
642
+ fossil_print("%s\n", zFilename);
643
+ }
644
+ }
645
+ }
646
+ sqlite3_finalize(pStmt);
647
+ sqlite3_close(db);
648
+ blob_zero(&out);
649
+ if( !bVerbose && !bQuiet && nFile>0 && zDest ){
650
+ fossil_print("%d files (%,lld bytes) written into %s",
651
+ nFile, nByte, zDest);
652
+ if( nDir>1 ){
653
+ fossil_print(" and %d subdirectories\n", nDir-1);
654
+ }else{
655
+ fossil_print("\n");
656
+ }
657
+ }
658
+}
434659
--- src/checkout.c
+++ src/checkout.c
@@ -19,10 +19,11 @@
19 ** from the local repository.
20 */
21 #include "config.h"
22 #include "checkout.h"
23 #include <assert.h>
 
24
25 /*
26 ** Check to see if there is an existing check-out that has been
27 ** modified. Return values:
28 **
@@ -429,5 +430,229 @@
429 }
430 unlink_local_database(1);
431 db_close(1);
432 unlink_local_database(0);
433 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
--- src/checkout.c
+++ src/checkout.c
@@ -19,10 +19,11 @@
19 ** from the local repository.
20 */
21 #include "config.h"
22 #include "checkout.h"
23 #include <assert.h>
24 #include <zlib.h>
25
26 /*
27 ** Check to see if there is an existing check-out that has been
28 ** modified. Return values:
29 **
@@ -429,5 +430,229 @@
430 }
431 unlink_local_database(1);
432 db_close(1);
433 unlink_local_database(0);
434 }
435
436
437 /*
438 ** COMMAND: get
439 **
440 ** Usage: %fossil get URL ?VERSION? ?OPTIONS?
441 **
442 ** Download a single check-in from a remote repository named URL and
443 ** unpack all of the files locally. The check-in is identified by VERSION.
444 **
445 ** URL can be a traditional URL like one of:
446 **
447 ** * https://domain.com/project
448 ** * ssh://my-server/project.fossil
449 ** * file:/home/user/Fossils/project.fossil
450 **
451 ** Or URL can be just the name of a local repository without the "file:"
452 ** prefix.
453 **
454 ** This command works by downloading an SQL archive of the requested
455 ** check-in and then extracting all the files from the archive.
456 **
457 ** Options:
458 ** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ."
459 ** to extract into the local directory.
460 **
461 ** -f|--force Overwrite existing files
462 **
463 ** --list List all the files that would have been checked
464 ** out but do not actually write anything to the
465 ** filesystem.
466 **
467 ** --sqlar ARCHIVE Store the check-out in an SQL-archive rather
468 ** than unpacking them into separate files.
469 **
470 ** -v|--verbose Show all files as they are extracted
471 */
472 void get_cmd(void){
473 int forceFlag = find_option("force","f",0)!=0;
474 int bVerbose = find_option("verbose","v",0)!=0;
475 int bQuiet = find_option("quiet","q",0)!=0;
476 int bDebug = find_option("debug",0,0)!=0;
477 int bList = find_option("list",0,0)!=0;
478 const char *zSqlArchive = find_option("sqlar",0,1);
479 const char *z;
480 char *zDest = 0; /* Where to store results */
481 char *zSql; /* SQL used to query the results */
482 const char *zUrl; /* Url to get */
483 const char *zVers; /* Version name to get */
484 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
485 Blob in, out; /* I/O for the HTTP request */
486 Blob file; /* A file to extract */
487 sqlite3 *db; /* Database containing downloaded sqlar */
488 sqlite3_stmt *pStmt; /* Statement for querying the database */
489 int rc; /* Result of subroutine calls */
490 int nFile = 0; /* Number of files written */
491 int nDir = 0; /* Number of directories written */
492 i64 nByte = 0; /* Number of bytes written */
493
494 z = find_option("dest",0,1);
495 if( z ) zDest = fossil_strdup(z);
496 verify_all_options();
497 if( g.argc<3 || g.argc>4 ){
498 usage("URL ?VERSION? ?OPTIONS?");
499 }
500 zUrl = g.argv[2];
501 zVers = g.argc==4 ? g.argv[3] : "trunk";
502
503 /* Parse the URL of the repository */
504 url_parse(zUrl, 0);
505
506 /* Construct an appropriate name for the destination directory */
507 if( zDest==0 ){
508 int i;
509 const char *zTail;
510 const char *zDot;
511 int n;
512 if( g.url.isFile ){
513 zTail = file_tail(g.url.name);
514 }else{
515 zTail = file_tail(g.url.path);
516 }
517 zDot = strchr(zTail,'.');
518 if( zDot==0 ) zDot = zTail+strlen(zTail);
519 n = (int)(zDot - zTail);
520 zDest = mprintf("%.*s-%s", n, zTail, zVers);
521 for(i=0; zDest[i]; i++){
522 char c = zDest[i];
523 if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
524 zDest[i] = '-';
525 }
526 }
527 }
528 if( bDebug ){
529 fossil_print("dest = %s\n", zDest);
530 }
531
532 /* Error checking */
533 if( zDest!=file_tail(zDest) ){
534 fossil_fatal("--dest must be a simple directory name, not a path");
535 }
536 if( zVers!=file_tail(zVers) ){
537 fossil_fatal("The \"fossil get\" command does not currently work with"
538 " version names that contain \"/\". This will be fixed in"
539 " a future release.");
540 }
541 /* To relax the restrictions above, change the subpath URL formula below
542 ** to use query parameters. Ex: /sqlar?r=%t&name=%t */
543
544 if( !forceFlag ){
545 if( zSqlArchive ){
546 if( file_isdir(zSqlArchive, ExtFILE)>0 ){
547 fossil_fatal("file already exists: \"%s\"", zSqlArchive);
548 }
549 }else if( file_isdir(zDest, ExtFILE)>0 ){
550 if( fossil_strcmp(zDest,".")==0 ){
551 if( file_directory_size(zDest,0,1) ){
552 fossil_fatal("current directory is not empty");
553 }
554 }else{
555 fossil_fatal("\"%s\" already exists", zDest);
556 }
557 }
558 }
559
560 /* Construct a subpath on the URL if necessary */
561 if( g.url.isFile ){
562 g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
563 }else{
564 g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
565 }
566
567 if( bDebug ){
568 urlparse_print(0);
569 }
570
571 /* Fetch the ZIP archive for the requested check-in */
572 blob_init(&in, 0, 0);
573 blob_init(&out, 0, 0);
574 if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
575 if( bQuiet ) mHttpFlags |= HTTP_QUIET;
576 rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
577 if( rc
578 || out.nUsed<512
579 || (out.nUsed%512)!=0
580 || memcmp(out.aData,"SQLite format 3",16)!=0
581 ){
582 fossil_fatal("Server did not return the requested check-in.");
583 }
584
585 if( zSqlArchive ){
586 blob_write_to_file(&out, zSqlArchive);
587 if( bVerbose ) fossil_print("%s\n", zSqlArchive);
588 return;
589 }
590
591 rc = sqlite3_open(":memory:", &db);
592 if( rc==SQLITE_OK ){
593 int sz = blob_size(&out);
594 rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
595 SQLITE_DESERIALIZE_READONLY);
596 }
597 if( rc!=SQLITE_OK ){
598 fossil_fatal("Cannot create an in-memory database: %s",
599 sqlite3_errmsg(db));
600 }
601 zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
602 " WHERE name GLOB '%q*'", zDest);
603 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
604 fossil_free(zSql);
605 if( rc!=0 ){
606 fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
607 }
608 blob_init(&file, 0, 0);
609 while( sqlite3_step(pStmt)==SQLITE_ROW ){
610 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
611 int mode = sqlite3_column_int(pStmt, 1);
612 int sz = sqlite3_column_int(pStmt, 2);
613 if( bList ){
614 fossil_print("%s\n", zFilename);
615 }else if( mode & 0x4000 ){
616 /* A directory name */
617 nDir++;
618 file_mkdir(zFilename, ExtFILE, 1);
619 }else{
620 /* A file */
621 unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
622 unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
623 unsigned long int nOut2 = (unsigned long int)sz;
624 nFile++;
625 nByte += sz;
626 blob_resize(&file, sz);
627 if( nIn<sz ){
628 rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
629 inBuf, nIn);
630 if( rc!=Z_OK ){
631 fossil_fatal("Failed to uncompress file %s", zFilename);
632 }
633 }else{
634 memcpy(blob_buffer(&file), inBuf, sz);
635 }
636 blob_write_to_file(&file, zFilename);
637 if( mode & 0x40 ){
638 file_setexe(zFilename, 1);
639 }
640 blob_zero(&file);
641 if( bVerbose ){
642 fossil_print("%s\n", zFilename);
643 }
644 }
645 }
646 sqlite3_finalize(pStmt);
647 sqlite3_close(db);
648 blob_zero(&out);
649 if( !bVerbose && !bQuiet && nFile>0 && zDest ){
650 fossil_print("%d files (%,lld bytes) written into %s",
651 nFile, nByte, zDest);
652 if( nDir>1 ){
653 fossil_print(" and %d subdirectories\n", nDir-1);
654 }else{
655 fossil_print("\n");
656 }
657 }
658 }
659
+1 -1
--- src/clearsign.c
+++ src/clearsign.c
@@ -65,11 +65,11 @@
6565
blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
6666
blob_appendf(pOut, "%s", blob_str(&tmpBlob));
6767
blob_zero(&tmpBlob);
6868
blob_read_from_file(&tmpBlob, zIn, ExtFILE);
6969
/* Add signature - already armored by SSH */
70
- blob_appendf(pOut, "%s", blob_str(&tmpBlob));
70
+ blob_appendb(pOut, &tmpBlob);
7171
}else{
7272
/* Assume that the external command creates non-detached signatures */
7373
blob_read_from_file(pOut, zIn, ExtFILE);
7474
}
7575
}else{
7676
--- src/clearsign.c
+++ src/clearsign.c
@@ -65,11 +65,11 @@
65 blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
66 blob_appendf(pOut, "%s", blob_str(&tmpBlob));
67 blob_zero(&tmpBlob);
68 blob_read_from_file(&tmpBlob, zIn, ExtFILE);
69 /* Add signature - already armored by SSH */
70 blob_appendf(pOut, "%s", blob_str(&tmpBlob));
71 }else{
72 /* Assume that the external command creates non-detached signatures */
73 blob_read_from_file(pOut, zIn, ExtFILE);
74 }
75 }else{
76
--- src/clearsign.c
+++ src/clearsign.c
@@ -65,11 +65,11 @@
65 blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
66 blob_appendf(pOut, "%s", blob_str(&tmpBlob));
67 blob_zero(&tmpBlob);
68 blob_read_from_file(&tmpBlob, zIn, ExtFILE);
69 /* Add signature - already armored by SSH */
70 blob_appendb(pOut, &tmpBlob);
71 }else{
72 /* Assume that the external command creates non-detached signatures */
73 blob_read_from_file(pOut, zIn, ExtFILE);
74 }
75 }else{
76
+7 -27
--- src/clone.c
+++ src/clone.c
@@ -405,40 +405,18 @@
405405
db_protect_pop();
406406
}
407407
}
408408
409409
/*
410
-** WEBPAGE: download
410
+** WEBPAGE: howtoclone
411411
**
412
-** Provide a simple page that enables newbies to download the latest tarball or
413
-** ZIP archive, and provides instructions on how to clone.
412
+** Provide instructions on how to clone this repository.
414413
*/
415
-void download_page(void){
414
+void howtoclone_page(void){
416415
login_check_credentials();
417416
cgi_check_for_malice();
418
- style_header("Download Page");
419
- if( !g.perm.Zip ){
420
- @ <p>Bummer. You do not have permission to download.
421
- if( g.zLogin==0 || g.zLogin[0]==0 ){
422
- @ Maybe it would work better if you
423
- @ %z(href("%R/login"))logged in</a>.
424
- }else{
425
- @ Contact the site administrator and ask them to give
426
- @ you "Download Zip" privileges.
427
- }
428
- }else{
429
- const char *zDLTag = db_get("download-tag","trunk");
430
- const char *zNm = db_get("short-project-name","download");
431
- char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
432
- @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
433
- zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
434
- @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
435
- if( g.zLogin!=0 ){
436
- zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
437
- @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
438
- }
439
- }
417
+ style_header("How To Clone This Repository");
440418
if( !g.perm.Clone ){
441419
@ <p>You are not authorized to clone this repository.
442420
if( g.zLogin==0 || g.zLogin[0]==0 ){
443421
@ Maybe you would be able to clone if you
444422
@ %z(href("%R/login"))logged in</a>.
@@ -446,12 +424,14 @@
446424
@ Contact the site administrator and ask them to give
447425
@ you "Clone" privileges in order to clone.
448426
}
449427
}else{
450428
const char *zNm = db_get("short-project-name","clone");
451
- @ <p>Clone the repository using this command:
429
+ @ <p>Clone this repository by running a command like the following:
452430
@ <blockquote><pre>
453431
@ fossil clone %s(g.zBaseURL) %h(zNm).fossil
454432
@ </pre></blockquote>
433
+ @ <p>Do a web search for "fossil clone" or similar to find additional
434
+ @ information about using a cloned Fossil repository.
455435
}
456436
style_finish_page();
457437
}
458438
--- src/clone.c
+++ src/clone.c
@@ -405,40 +405,18 @@
405 db_protect_pop();
406 }
407 }
408
409 /*
410 ** WEBPAGE: download
411 **
412 ** Provide a simple page that enables newbies to download the latest tarball or
413 ** ZIP archive, and provides instructions on how to clone.
414 */
415 void download_page(void){
416 login_check_credentials();
417 cgi_check_for_malice();
418 style_header("Download Page");
419 if( !g.perm.Zip ){
420 @ <p>Bummer. You do not have permission to download.
421 if( g.zLogin==0 || g.zLogin[0]==0 ){
422 @ Maybe it would work better if you
423 @ %z(href("%R/login"))logged in</a>.
424 }else{
425 @ Contact the site administrator and ask them to give
426 @ you "Download Zip" privileges.
427 }
428 }else{
429 const char *zDLTag = db_get("download-tag","trunk");
430 const char *zNm = db_get("short-project-name","download");
431 char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
432 @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
433 zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
434 @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
435 if( g.zLogin!=0 ){
436 zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
437 @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
438 }
439 }
440 if( !g.perm.Clone ){
441 @ <p>You are not authorized to clone this repository.
442 if( g.zLogin==0 || g.zLogin[0]==0 ){
443 @ Maybe you would be able to clone if you
444 @ %z(href("%R/login"))logged in</a>.
@@ -446,12 +424,14 @@
446 @ Contact the site administrator and ask them to give
447 @ you "Clone" privileges in order to clone.
448 }
449 }else{
450 const char *zNm = db_get("short-project-name","clone");
451 @ <p>Clone the repository using this command:
452 @ <blockquote><pre>
453 @ fossil clone %s(g.zBaseURL) %h(zNm).fossil
454 @ </pre></blockquote>
 
 
455 }
456 style_finish_page();
457 }
458
--- src/clone.c
+++ src/clone.c
@@ -405,40 +405,18 @@
405 db_protect_pop();
406 }
407 }
408
409 /*
410 ** WEBPAGE: howtoclone
411 **
412 ** Provide instructions on how to clone this repository.
 
413 */
414 void howtoclone_page(void){
415 login_check_credentials();
416 cgi_check_for_malice();
417 style_header("How To Clone This Repository");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418 if( !g.perm.Clone ){
419 @ <p>You are not authorized to clone this repository.
420 if( g.zLogin==0 || g.zLogin[0]==0 ){
421 @ Maybe you would be able to clone if you
422 @ %z(href("%R/login"))logged in</a>.
@@ -446,12 +424,14 @@
424 @ Contact the site administrator and ask them to give
425 @ you "Clone" privileges in order to clone.
426 }
427 }else{
428 const char *zNm = db_get("short-project-name","clone");
429 @ <p>Clone this repository by running a command like the following:
430 @ <blockquote><pre>
431 @ fossil clone %s(g.zBaseURL) %h(zNm).fossil
432 @ </pre></blockquote>
433 @ <p>Do a web search for "fossil clone" or similar to find additional
434 @ information about using a cloned Fossil repository.
435 }
436 style_finish_page();
437 }
438
+1 -1
--- src/comformat.c
+++ src/comformat.c
@@ -303,11 +303,11 @@
303303
case 4:
304304
*pUtf32 =
305305
( (z[0] & 0x0f)<<18 ) |
306306
( (z[1] & 0x3f)<<12 ) |
307307
( (z[2] & 0x3f)<< 6 ) |
308
- ( (z[4] & 0x3f)<< 0 ) ;
308
+ ( (z[3] & 0x3f)<< 0 ) ;
309309
break;
310310
case 3:
311311
*pUtf32 =
312312
( (z[0] & 0x0f)<<12 ) |
313313
( (z[1] & 0x3f)<< 6 ) |
314314
--- src/comformat.c
+++ src/comformat.c
@@ -303,11 +303,11 @@
303 case 4:
304 *pUtf32 =
305 ( (z[0] & 0x0f)<<18 ) |
306 ( (z[1] & 0x3f)<<12 ) |
307 ( (z[2] & 0x3f)<< 6 ) |
308 ( (z[4] & 0x3f)<< 0 ) ;
309 break;
310 case 3:
311 *pUtf32 =
312 ( (z[0] & 0x0f)<<12 ) |
313 ( (z[1] & 0x3f)<< 6 ) |
314
--- src/comformat.c
+++ src/comformat.c
@@ -303,11 +303,11 @@
303 case 4:
304 *pUtf32 =
305 ( (z[0] & 0x0f)<<18 ) |
306 ( (z[1] & 0x3f)<<12 ) |
307 ( (z[2] & 0x3f)<< 6 ) |
308 ( (z[3] & 0x3f)<< 0 ) ;
309 break;
310 case 3:
311 *pUtf32 =
312 ( (z[0] & 0x0f)<<12 ) |
313 ( (z[1] & 0x3f)<< 6 ) |
314
+2 -1
--- src/content.c
+++ src/content.c
@@ -600,10 +600,11 @@
600600
);
601601
db_bind_blob(&s1, ":data", &cmpr);
602602
db_exec(&s1);
603603
rid = db_last_insert_rowid();
604604
if( !pBlob ){
605
+ assert(!"cannot happen: pBlob is always non-NULL");
605606
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
606607
}
607608
}
608609
if( g.markPrivate || isPrivate ){
609610
db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
@@ -863,11 +864,11 @@
863864
int srcid = aSrc[i];
864865
if( srcid==rid ) continue;
865866
if( content_is_private(srcid) && !content_is_private(rid) ) continue;
866867
867868
/* Compute all ancestors of srcid and make sure rid is not one of them.
868
- ** If rid is an ancestor of srcid, then making rid a decendent of srcid
869
+ ** If rid is an ancestor of srcid, then making rid a descendent of srcid
869870
** would create a delta loop. */
870871
s = srcid;
871872
while( (s = delta_source_rid(s))>0 ){
872873
if( s==rid ){
873874
content_undelta(srcid);
874875
--- src/content.c
+++ src/content.c
@@ -600,10 +600,11 @@
600 );
601 db_bind_blob(&s1, ":data", &cmpr);
602 db_exec(&s1);
603 rid = db_last_insert_rowid();
604 if( !pBlob ){
 
605 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
606 }
607 }
608 if( g.markPrivate || isPrivate ){
609 db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
@@ -863,11 +864,11 @@
863 int srcid = aSrc[i];
864 if( srcid==rid ) continue;
865 if( content_is_private(srcid) && !content_is_private(rid) ) continue;
866
867 /* Compute all ancestors of srcid and make sure rid is not one of them.
868 ** If rid is an ancestor of srcid, then making rid a decendent of srcid
869 ** would create a delta loop. */
870 s = srcid;
871 while( (s = delta_source_rid(s))>0 ){
872 if( s==rid ){
873 content_undelta(srcid);
874
--- src/content.c
+++ src/content.c
@@ -600,10 +600,11 @@
600 );
601 db_bind_blob(&s1, ":data", &cmpr);
602 db_exec(&s1);
603 rid = db_last_insert_rowid();
604 if( !pBlob ){
605 assert(!"cannot happen: pBlob is always non-NULL");
606 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
607 }
608 }
609 if( g.markPrivate || isPrivate ){
610 db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
@@ -863,11 +864,11 @@
864 int srcid = aSrc[i];
865 if( srcid==rid ) continue;
866 if( content_is_private(srcid) && !content_is_private(rid) ) continue;
867
868 /* Compute all ancestors of srcid and make sure rid is not one of them.
869 ** If rid is an ancestor of srcid, then making rid a descendent of srcid
870 ** would create a delta loop. */
871 s = srcid;
872 while( (s = delta_source_rid(s))>0 ){
873 if( s==rid ){
874 content_undelta(srcid);
875
+7 -1
--- src/cookies.c
+++ src/cookies.c
@@ -256,11 +256,13 @@
256256
if( isQP ) continue;
257257
if( fossil_isupper(zName[0]) ) continue;
258258
if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
259259
zDel = mprintf("del%s",zName);
260260
if( P(zDel)!=0 ){
261
- cgi_set_cookie(zName, "", 0, -1);
261
+ const char *zPath = fossil_strcmp(ROBOT_COOKIE,zName)==0
262
+ ? "/" : 0;
263
+ cgi_set_cookie(zName, "", zPath, -1);
262264
cgi_redirect(g.zPath);
263265
}
264266
nCookie++;
265267
@ <li><p><b>%h(zName)</b>: %h(zValue)
266268
@ <input type="submit" name="%h(zDel)" value="Delete">
@@ -282,10 +284,14 @@
282284
&& hex_prefix_length(&zName[7])==16
283285
&& hex_prefix_length(zValue)>24
284286
){
285287
@ <p>This appears to be a login cookie for another Fossil repository
286288
@ in the same website.
289
+ }else
290
+ if( fossil_strcmp(zName, ROBOT_COOKIE)==0 ){
291
+ @ <p>This cookie shows that your web-browser has been tested is
292
+ @ believed to be operated by a human, not a robot.
287293
}
288294
else {
289295
@ <p>This cookie was not generated by Fossil. It might be something
290296
@ from another program on the same website.
291297
}
292298
--- src/cookies.c
+++ src/cookies.c
@@ -256,11 +256,13 @@
256 if( isQP ) continue;
257 if( fossil_isupper(zName[0]) ) continue;
258 if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
259 zDel = mprintf("del%s",zName);
260 if( P(zDel)!=0 ){
261 cgi_set_cookie(zName, "", 0, -1);
 
 
262 cgi_redirect(g.zPath);
263 }
264 nCookie++;
265 @ <li><p><b>%h(zName)</b>: %h(zValue)
266 @ <input type="submit" name="%h(zDel)" value="Delete">
@@ -282,10 +284,14 @@
282 && hex_prefix_length(&zName[7])==16
283 && hex_prefix_length(zValue)>24
284 ){
285 @ <p>This appears to be a login cookie for another Fossil repository
286 @ in the same website.
 
 
 
 
287 }
288 else {
289 @ <p>This cookie was not generated by Fossil. It might be something
290 @ from another program on the same website.
291 }
292
--- src/cookies.c
+++ src/cookies.c
@@ -256,11 +256,13 @@
256 if( isQP ) continue;
257 if( fossil_isupper(zName[0]) ) continue;
258 if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
259 zDel = mprintf("del%s",zName);
260 if( P(zDel)!=0 ){
261 const char *zPath = fossil_strcmp(ROBOT_COOKIE,zName)==0
262 ? "/" : 0;
263 cgi_set_cookie(zName, "", zPath, -1);
264 cgi_redirect(g.zPath);
265 }
266 nCookie++;
267 @ <li><p><b>%h(zName)</b>: %h(zValue)
268 @ <input type="submit" name="%h(zDel)" value="Delete">
@@ -282,10 +284,14 @@
284 && hex_prefix_length(&zName[7])==16
285 && hex_prefix_length(zValue)>24
286 ){
287 @ <p>This appears to be a login cookie for another Fossil repository
288 @ in the same website.
289 }else
290 if( fossil_strcmp(zName, ROBOT_COOKIE)==0 ){
291 @ <p>This cookie shows that your web-browser has been tested is
292 @ believed to be operated by a human, not a robot.
293 }
294 else {
295 @ <p>This cookie was not generated by Fossil. It might be something
296 @ from another program on the same website.
297 }
298
+19 -22
--- src/copybtn.js
+++ src/copybtn.js
@@ -1,32 +1,34 @@
11
/* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
22
** thereof) of the target elements to the clipboard.
33
**
4
-** Newly created buttons are <span> elements with an SVG background icon,
5
-** defined by the "copy-button" class in the default CSS style sheet, and are
6
-** assigned the element ID "copy-<idTarget>".
7
-**
8
-** To simplify customization, the only properties modified for HTML-defined
9
-** buttons are the "onclick" handler, and the "transition" and "opacity" styles
10
-** (used for animation).
4
+** Newly created buttons are <button> elements plus a nested <span> element with
5
+** an SVG background icon, defined by the "copy-button" class in the default CSS
6
+** style sheet, and are assigned the element ID "copy-<idTarget>".
117
**
128
** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
139
** needs to be called to attach the "onclick" handler (done automatically from
14
-** a handler attached to the "DOMContentLoaded" event).
10
+** a handler attached to the "DOMContentLoaded" event). These functions create
11
+** the nested <span> element if the <button> element has no child nodes. Using
12
+** static HTML for the <span> element ensures the buttons are visible if there
13
+** are script errors, which may be useful for Fossil JS hackers (as good parts
14
+** of the Fossil web UI come down on JS errors, anyway).
1515
**
1616
** The initialization functions do not overwrite the "data-copytarget" and
1717
** "data-copylength" attributes with empty or null values for <idTarget> and
1818
** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
1919
** previous copy length limit.
2020
**
2121
** HTML snippet for statically created buttons:
2222
**
23
-** <span class="copy-button" id="copy-<idTarget>"
24
-** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span>
23
+** <button class="copy-button" id="copy-<idTarget>"
24
+** data-copytarget="<idTarget>" data-copylength="<cchLength>">
25
+** <span></span>
26
+** </button>
2527
*/
2628
function makeCopyButton(idTarget,bFlipped,cchLength){
27
- var elButton = document.createElement("span");
29
+ var elButton = document.createElement("button");
2830
elButton.className = "copy-button";
2931
if( bFlipped ) elButton.className += " copy-button-flipped";
3032
elButton.id = "copy-" + idTarget;
3133
initCopyButton(elButton,idTarget,cchLength);
3234
return elButton;
@@ -36,15 +38,18 @@
3638
var elButton = document.getElementById(idButton);
3739
if( elButton ) initCopyButton(elButton,idTarget,cchLength);
3840
return elButton;
3941
}
4042
function initCopyButton(elButton,idTarget,cchLength){
41
- elButton.style.transition = "";
42
- elButton.style.opacity = 1;
4343
if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
4444
if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
4545
elButton.onclick = clickCopyButton;
46
+ /* Make sure the <button> contains a single nested <span>. */
47
+ if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){
48
+ while( elButton.firstChild ) elButton.removeChild(elButton.lastChild);
49
+ elButton.appendChild(document.createElement("span"));
50
+ }
4651
return elButton;
4752
}
4853
setTimeout(function(){
4954
var elButtons = document.getElementsByClassName("copy-button");
5055
for ( var i=0; i<elButtons.length; i++ ){
@@ -53,14 +58,11 @@
5358
},1);
5459
/* The onclick handler for the "Copy Button". */
5560
function clickCopyButton(e){
5661
e.preventDefault(); /* Mandatory for <a> and <button>. */
5762
e.stopPropagation();
58
- if( this.getAttribute("data-copylocked") ) return;
59
- this.setAttribute("data-copylocked","1");
60
- this.style.transition = "opacity 400ms ease-in-out";
61
- this.style.opacity = 0;
63
+ if( this.disabled ) return; /* This check is probably redundant. */
6264
var idTarget = this.getAttribute("data-copytarget");
6365
var elTarget = document.getElementById(idTarget);
6466
if( elTarget ){
6567
var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
6668
var cchLength = parseInt(this.getAttribute("data-copylength"));
@@ -67,15 +69,10 @@
6769
if( !isNaN(cchLength) && cchLength>0 ){
6870
text = text.slice(0,cchLength); /* Assume single-byte chars. */
6971
}
7072
copyTextToClipboard(text);
7173
}
72
- setTimeout(function(){
73
- this.style.transition = "";
74
- this.style.opacity = 1;
75
- this.removeAttribute("data-copylocked");
76
- }.bind(this),400);
7774
}
7875
/* Create a temporary <textarea> element and copy the contents to clipboard. */
7976
function copyTextToClipboard(text){
8077
if( window.clipboardData && window.clipboardData.setData ){
8178
window.clipboardData.setData("Text",text);
8279
--- src/copybtn.js
+++ src/copybtn.js
@@ -1,32 +1,34 @@
1 /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
2 ** thereof) of the target elements to the clipboard.
3 **
4 ** Newly created buttons are <span> elements with an SVG background icon,
5 ** defined by the "copy-button" class in the default CSS style sheet, and are
6 ** assigned the element ID "copy-<idTarget>".
7 **
8 ** To simplify customization, the only properties modified for HTML-defined
9 ** buttons are the "onclick" handler, and the "transition" and "opacity" styles
10 ** (used for animation).
11 **
12 ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
13 ** needs to be called to attach the "onclick" handler (done automatically from
14 ** a handler attached to the "DOMContentLoaded" event).
 
 
 
 
15 **
16 ** The initialization functions do not overwrite the "data-copytarget" and
17 ** "data-copylength" attributes with empty or null values for <idTarget> and
18 ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
19 ** previous copy length limit.
20 **
21 ** HTML snippet for statically created buttons:
22 **
23 ** <span class="copy-button" id="copy-<idTarget>"
24 ** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span>
 
 
25 */
26 function makeCopyButton(idTarget,bFlipped,cchLength){
27 var elButton = document.createElement("span");
28 elButton.className = "copy-button";
29 if( bFlipped ) elButton.className += " copy-button-flipped";
30 elButton.id = "copy-" + idTarget;
31 initCopyButton(elButton,idTarget,cchLength);
32 return elButton;
@@ -36,15 +38,18 @@
36 var elButton = document.getElementById(idButton);
37 if( elButton ) initCopyButton(elButton,idTarget,cchLength);
38 return elButton;
39 }
40 function initCopyButton(elButton,idTarget,cchLength){
41 elButton.style.transition = "";
42 elButton.style.opacity = 1;
43 if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
44 if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
45 elButton.onclick = clickCopyButton;
 
 
 
 
 
46 return elButton;
47 }
48 setTimeout(function(){
49 var elButtons = document.getElementsByClassName("copy-button");
50 for ( var i=0; i<elButtons.length; i++ ){
@@ -53,14 +58,11 @@
53 },1);
54 /* The onclick handler for the "Copy Button". */
55 function clickCopyButton(e){
56 e.preventDefault(); /* Mandatory for <a> and <button>. */
57 e.stopPropagation();
58 if( this.getAttribute("data-copylocked") ) return;
59 this.setAttribute("data-copylocked","1");
60 this.style.transition = "opacity 400ms ease-in-out";
61 this.style.opacity = 0;
62 var idTarget = this.getAttribute("data-copytarget");
63 var elTarget = document.getElementById(idTarget);
64 if( elTarget ){
65 var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
66 var cchLength = parseInt(this.getAttribute("data-copylength"));
@@ -67,15 +69,10 @@
67 if( !isNaN(cchLength) && cchLength>0 ){
68 text = text.slice(0,cchLength); /* Assume single-byte chars. */
69 }
70 copyTextToClipboard(text);
71 }
72 setTimeout(function(){
73 this.style.transition = "";
74 this.style.opacity = 1;
75 this.removeAttribute("data-copylocked");
76 }.bind(this),400);
77 }
78 /* Create a temporary <textarea> element and copy the contents to clipboard. */
79 function copyTextToClipboard(text){
80 if( window.clipboardData && window.clipboardData.setData ){
81 window.clipboardData.setData("Text",text);
82
--- src/copybtn.js
+++ src/copybtn.js
@@ -1,32 +1,34 @@
1 /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
2 ** thereof) of the target elements to the clipboard.
3 **
4 ** Newly created buttons are <button> elements plus a nested <span> element with
5 ** an SVG background icon, defined by the "copy-button" class in the default CSS
6 ** style sheet, and are assigned the element ID "copy-<idTarget>".
 
 
 
 
7 **
8 ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
9 ** needs to be called to attach the "onclick" handler (done automatically from
10 ** a handler attached to the "DOMContentLoaded" event). These functions create
11 ** the nested <span> element if the <button> element has no child nodes. Using
12 ** static HTML for the <span> element ensures the buttons are visible if there
13 ** are script errors, which may be useful for Fossil JS hackers (as good parts
14 ** of the Fossil web UI come down on JS errors, anyway).
15 **
16 ** The initialization functions do not overwrite the "data-copytarget" and
17 ** "data-copylength" attributes with empty or null values for <idTarget> and
18 ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
19 ** previous copy length limit.
20 **
21 ** HTML snippet for statically created buttons:
22 **
23 ** <button class="copy-button" id="copy-<idTarget>"
24 ** data-copytarget="<idTarget>" data-copylength="<cchLength>">
25 ** <span></span>
26 ** </button>
27 */
28 function makeCopyButton(idTarget,bFlipped,cchLength){
29 var elButton = document.createElement("button");
30 elButton.className = "copy-button";
31 if( bFlipped ) elButton.className += " copy-button-flipped";
32 elButton.id = "copy-" + idTarget;
33 initCopyButton(elButton,idTarget,cchLength);
34 return elButton;
@@ -36,15 +38,18 @@
38 var elButton = document.getElementById(idButton);
39 if( elButton ) initCopyButton(elButton,idTarget,cchLength);
40 return elButton;
41 }
42 function initCopyButton(elButton,idTarget,cchLength){
 
 
43 if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
44 if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
45 elButton.onclick = clickCopyButton;
46 /* Make sure the <button> contains a single nested <span>. */
47 if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){
48 while( elButton.firstChild ) elButton.removeChild(elButton.lastChild);
49 elButton.appendChild(document.createElement("span"));
50 }
51 return elButton;
52 }
53 setTimeout(function(){
54 var elButtons = document.getElementsByClassName("copy-button");
55 for ( var i=0; i<elButtons.length; i++ ){
@@ -53,14 +58,11 @@
58 },1);
59 /* The onclick handler for the "Copy Button". */
60 function clickCopyButton(e){
61 e.preventDefault(); /* Mandatory for <a> and <button>. */
62 e.stopPropagation();
63 if( this.disabled ) return; /* This check is probably redundant. */
 
 
 
64 var idTarget = this.getAttribute("data-copytarget");
65 var elTarget = document.getElementById(idTarget);
66 if( elTarget ){
67 var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
68 var cchLength = parseInt(this.getAttribute("data-copylength"));
@@ -67,15 +69,10 @@
69 if( !isNaN(cchLength) && cchLength>0 ){
70 text = text.slice(0,cchLength); /* Assume single-byte chars. */
71 }
72 copyTextToClipboard(text);
73 }
 
 
 
 
 
74 }
75 /* Create a temporary <textarea> element and copy the contents to clipboard. */
76 function copyTextToClipboard(text){
77 if( window.clipboardData && window.clipboardData.setData ){
78 window.clipboardData.setData("Text",text);
79
+26 -4
--- src/db.c
+++ src/db.c
@@ -125,10 +125,20 @@
125125
#endif /* FOSSIL_ENABLE_JSON */
126126
if( g.xferPanic && g.cgiOutput==1 ){
127127
cgi_reset_content();
128128
@ error Database\serror:\s%F(z)
129129
cgi_reply();
130
+ }
131
+ if( strstr(z,"attempt to write a readonly database") ){
132
+ static const char *azDbNames[] = { "repository", "localdb", "configdb" };
133
+ int i;
134
+ for(i=0; i<3; i++){
135
+ if( sqlite3_db_readonly(g.db, azDbNames[i])==1 ){
136
+ z = mprintf("\"%s\" is readonly.\n%s",
137
+ sqlite3_db_filename(g.db,azDbNames[i]), z);
138
+ }
139
+ }
130140
}
131141
fossil_fatal("Database error: %s", z);
132142
}
133143
134144
/*
@@ -1218,12 +1228,12 @@
12181228
}
12191229
12201230
/*
12211231
** Execute a query. Return the first column of the first row
12221232
** of the result set as a string. Space to hold the string is
1223
-** obtained from malloc(). If the result set is empty, return
1224
-** zDefault instead.
1233
+** obtained from fossil_strdup() and should be freed using fossil_free().
1234
+** If the result set is empty, return a copy of zDefault instead.
12251235
*/
12261236
char *db_text(const char *zDefault, const char *zSql, ...){
12271237
va_list ap;
12281238
Stmt s;
12291239
char *z;
@@ -3589,16 +3599,16 @@
35893599
35903600
/*
35913601
** Return true if the string zVal represents "true" (or "false").
35923602
*/
35933603
int is_truth(const char *zVal){
3594
- static const char *const azOn[] = { "on", "yes", "true", "1" };
3604
+ static const char *const azOn[] = { "on", "yes", "true" };
35953605
int i;
35963606
for(i=0; i<count(azOn); i++){
35973607
if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
35983608
}
3599
- return 0;
3609
+ return atoi(zVal);
36003610
}
36013611
int is_false(const char *zVal){
36023612
static const char *const azOff[] = { "off", "no", "false", "0" };
36033613
int i;
36043614
for(i=0; i<count(azOff); i++){
@@ -4250,10 +4260,13 @@
42504260
** them).
42514261
** --verbose If passed a URI then this flag is passed on to the clone
42524262
** operation, otherwise it has no effect
42534263
** --workdir DIR Use DIR as the working directory instead of ".". The DIR
42544264
** directory is created if it does not exist.
4265
+** --reopen REPOFILE Changes the repository file used by the current checkout
4266
+** to REPOFILE. Use this after moving a checkout's
4267
+** repository. This may lose stash and bisect history.
42554268
**
42564269
** See also: [[close]], [[clone]]
42574270
*/
42584271
void cmd_open(void){
42594272
int emptyFlag;
@@ -4264,14 +4277,23 @@
42644277
int bForce = 0; /* --force. Open even if non-empty dir */
42654278
static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
42664279
const char *zWorkDir; /* --workdir value */
42674280
const char *zRepo = 0; /* Name of the repository file */
42684281
const char *zRepoDir = 0; /* --repodir value */
4282
+ const char *zReopen = 0; /* --reopen REPOFILE */
42694283
char *zPwd; /* Initial working directory */
42704284
int isUri = 0; /* True if REPOSITORY is a URI */
42714285
int nLocal; /* Number of preexisting files in cwd */
42724286
int bVerbose = 0; /* --verbose option for clone */
4287
+
4288
+ zReopen = find_option("reopen",0,1);
4289
+ if( 0!=zReopen ){
4290
+ g.argc = 3;
4291
+ g.argv[2] = (char*)zReopen;
4292
+ move_repo_cmd();
4293
+ return;
4294
+ }
42734295
42744296
url_proxy_options();
42754297
emptyFlag = find_option("empty",0,0)!=0;
42764298
keepFlag = find_option("keep","k",0)!=0;
42774299
forceMissingFlag = find_option("force-missing",0,0)!=0;
42784300
--- src/db.c
+++ src/db.c
@@ -125,10 +125,20 @@
125 #endif /* FOSSIL_ENABLE_JSON */
126 if( g.xferPanic && g.cgiOutput==1 ){
127 cgi_reset_content();
128 @ error Database\serror:\s%F(z)
129 cgi_reply();
 
 
 
 
 
 
 
 
 
 
130 }
131 fossil_fatal("Database error: %s", z);
132 }
133
134 /*
@@ -1218,12 +1228,12 @@
1218 }
1219
1220 /*
1221 ** Execute a query. Return the first column of the first row
1222 ** of the result set as a string. Space to hold the string is
1223 ** obtained from malloc(). If the result set is empty, return
1224 ** zDefault instead.
1225 */
1226 char *db_text(const char *zDefault, const char *zSql, ...){
1227 va_list ap;
1228 Stmt s;
1229 char *z;
@@ -3589,16 +3599,16 @@
3589
3590 /*
3591 ** Return true if the string zVal represents "true" (or "false").
3592 */
3593 int is_truth(const char *zVal){
3594 static const char *const azOn[] = { "on", "yes", "true", "1" };
3595 int i;
3596 for(i=0; i<count(azOn); i++){
3597 if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
3598 }
3599 return 0;
3600 }
3601 int is_false(const char *zVal){
3602 static const char *const azOff[] = { "off", "no", "false", "0" };
3603 int i;
3604 for(i=0; i<count(azOff); i++){
@@ -4250,10 +4260,13 @@
4250 ** them).
4251 ** --verbose If passed a URI then this flag is passed on to the clone
4252 ** operation, otherwise it has no effect
4253 ** --workdir DIR Use DIR as the working directory instead of ".". The DIR
4254 ** directory is created if it does not exist.
 
 
 
4255 **
4256 ** See also: [[close]], [[clone]]
4257 */
4258 void cmd_open(void){
4259 int emptyFlag;
@@ -4264,14 +4277,23 @@
4264 int bForce = 0; /* --force. Open even if non-empty dir */
4265 static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
4266 const char *zWorkDir; /* --workdir value */
4267 const char *zRepo = 0; /* Name of the repository file */
4268 const char *zRepoDir = 0; /* --repodir value */
 
4269 char *zPwd; /* Initial working directory */
4270 int isUri = 0; /* True if REPOSITORY is a URI */
4271 int nLocal; /* Number of preexisting files in cwd */
4272 int bVerbose = 0; /* --verbose option for clone */
 
 
 
 
 
 
 
 
4273
4274 url_proxy_options();
4275 emptyFlag = find_option("empty",0,0)!=0;
4276 keepFlag = find_option("keep","k",0)!=0;
4277 forceMissingFlag = find_option("force-missing",0,0)!=0;
4278
--- src/db.c
+++ src/db.c
@@ -125,10 +125,20 @@
125 #endif /* FOSSIL_ENABLE_JSON */
126 if( g.xferPanic && g.cgiOutput==1 ){
127 cgi_reset_content();
128 @ error Database\serror:\s%F(z)
129 cgi_reply();
130 }
131 if( strstr(z,"attempt to write a readonly database") ){
132 static const char *azDbNames[] = { "repository", "localdb", "configdb" };
133 int i;
134 for(i=0; i<3; i++){
135 if( sqlite3_db_readonly(g.db, azDbNames[i])==1 ){
136 z = mprintf("\"%s\" is readonly.\n%s",
137 sqlite3_db_filename(g.db,azDbNames[i]), z);
138 }
139 }
140 }
141 fossil_fatal("Database error: %s", z);
142 }
143
144 /*
@@ -1218,12 +1228,12 @@
1228 }
1229
1230 /*
1231 ** Execute a query. Return the first column of the first row
1232 ** of the result set as a string. Space to hold the string is
1233 ** obtained from fossil_strdup() and should be freed using fossil_free().
1234 ** If the result set is empty, return a copy of zDefault instead.
1235 */
1236 char *db_text(const char *zDefault, const char *zSql, ...){
1237 va_list ap;
1238 Stmt s;
1239 char *z;
@@ -3589,16 +3599,16 @@
3599
3600 /*
3601 ** Return true if the string zVal represents "true" (or "false").
3602 */
3603 int is_truth(const char *zVal){
3604 static const char *const azOn[] = { "on", "yes", "true" };
3605 int i;
3606 for(i=0; i<count(azOn); i++){
3607 if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
3608 }
3609 return atoi(zVal);
3610 }
3611 int is_false(const char *zVal){
3612 static const char *const azOff[] = { "off", "no", "false", "0" };
3613 int i;
3614 for(i=0; i<count(azOff); i++){
@@ -4250,10 +4260,13 @@
4260 ** them).
4261 ** --verbose If passed a URI then this flag is passed on to the clone
4262 ** operation, otherwise it has no effect
4263 ** --workdir DIR Use DIR as the working directory instead of ".". The DIR
4264 ** directory is created if it does not exist.
4265 ** --reopen REPOFILE Changes the repository file used by the current checkout
4266 ** to REPOFILE. Use this after moving a checkout's
4267 ** repository. This may lose stash and bisect history.
4268 **
4269 ** See also: [[close]], [[clone]]
4270 */
4271 void cmd_open(void){
4272 int emptyFlag;
@@ -4264,14 +4277,23 @@
4277 int bForce = 0; /* --force. Open even if non-empty dir */
4278 static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
4279 const char *zWorkDir; /* --workdir value */
4280 const char *zRepo = 0; /* Name of the repository file */
4281 const char *zRepoDir = 0; /* --repodir value */
4282 const char *zReopen = 0; /* --reopen REPOFILE */
4283 char *zPwd; /* Initial working directory */
4284 int isUri = 0; /* True if REPOSITORY is a URI */
4285 int nLocal; /* Number of preexisting files in cwd */
4286 int bVerbose = 0; /* --verbose option for clone */
4287
4288 zReopen = find_option("reopen",0,1);
4289 if( 0!=zReopen ){
4290 g.argc = 3;
4291 g.argv[2] = (char*)zReopen;
4292 move_repo_cmd();
4293 return;
4294 }
4295
4296 url_proxy_options();
4297 emptyFlag = find_option("empty",0,0)!=0;
4298 keepFlag = find_option("keep","k",0)!=0;
4299 forceMissingFlag = find_option("force-missing",0,0)!=0;
4300
+46 -8
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
11
/* This CSS file holds the default implementations for all of fossil's
22
CSS classes. When /style.css is requested, the rules in this file
33
are emitted first, followed by (1) page-specific CSS (if any) and
44
(2) skin-specific CSS.
55
*/
6
+body {
7
+ z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
8
+}
69
div.sidebox {
710
float: right;
811
background-color: white;
912
border-width: medium;
1013
border-style: double;
@@ -54,10 +57,13 @@
5457
border-width: 0;
5558
}
5659
span.timelineLeaf {
5760
font-weight: bold;
5861
}
62
+span.timelineHash {
63
+ font-weight: bold;
64
+}
5965
span.timelineHistDsp {
6066
font-weight: bold;
6167
}
6268
td.timelineTime {
6369
vertical-align: top;
@@ -557,10 +563,11 @@
557563
table.diff {
558564
width: 100%;
559565
border-spacing: 0;
560566
border-radius: 5px;
561567
border: 1px solid black;
568
+ overflow: hidden; /* Prevent background from overlapping rounded borders. */
562569
font-size: 80%;
563570
}
564571
table.diff td.diffln{
565572
padding: 0;
566573
}
@@ -748,10 +755,22 @@
748755
border-bottom: 3px solid gold;
749756
}
750757
body.tkt div.content ol.tkt-changes > li:target > ol {
751758
border-left: 1px solid gold;
752759
}
760
+body.tkt .tktCommentArea {
761
+ display: flex;
762
+ flex-direction: column;
763
+}
764
+body.tkt .newest-first-controls {
765
+ display: flex;
766
+ flex-direction: row;
767
+ flex-wrap: nowrap;
768
+}
769
+body.tkt .tktCommentArea.reverse {
770
+ flex-direction: column-reverse;
771
+}
753772
body.cpage-ckout .file-change-line,
754773
body.cpage-info .file-change-line,
755774
body.cpage-vinfo .file-change-line,
756775
body.cpage-ci .file-change-line,
757776
body.cpage-vdiff .file-change-line {
@@ -1136,19 +1155,40 @@
11361155
white-space: nowrap;
11371156
}
11381157
label[for] {
11391158
cursor: pointer;
11401159
}
1141
-.copy-button {
1142
- display: inline-block;
1160
+button.copy-button,
1161
+button.copy-button:hover,
1162
+button.copy-button:focus,
1163
+button.copy-button:active {
11431164
width: 14px;
11441165
height: 14px;
11451166
/*Note: .24em is slightly smaller than the average width of a normal space.*/
11461167
margin: -2px .24em 0 0;
11471168
padding: 0;
11481169
border: 0;
1170
+ outline: 0;
1171
+ background: none;
1172
+ font-size: inherit; /* Required for horizontal spacing. */
11491173
vertical-align: middle;
1174
+ user-select: none;
1175
+ cursor: pointer;
1176
+}
1177
+button.copy-button-flipped,
1178
+button.copy-button-flipped:hover,
1179
+button.copy-button-flipped:focus,
1180
+button.copy-button-flipped:active {
1181
+ margin: -2px 0 0 .24em;
1182
+}
1183
+button.copy-button span {
1184
+ display: block;
1185
+ width: 100%;
1186
+ height: 100%;
1187
+ margin: 0;
1188
+ padding: 0;
1189
+ border: 0;
11501190
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
11511191
viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
11521192
d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
11531193
d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
11541194
d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1159,19 +1199,17 @@
11591199
d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
11601200
background-repeat: no-repeat;
11611201
background-position: center;
11621202
cursor: pointer;
11631203
}
1164
-.copy-button.disabled {
1204
+button.copy-button:enabled:active span {
1205
+ background-size: 90%;
1206
+}
1207
+button.copy-button:disabled span {
11651208
filter: grayscale(1);
11661209
opacity: 0.4;
11671210
}
1168
-.copy-button-flipped {
1169
-/*Note: .16em is suitable for element grouping.*/
1170
- margin-left: .16em;
1171
- margin-right: 0;
1172
-}
11731211
.nobr {
11741212
white-space: nowrap;
11751213
}
11761214
.accordion {
11771215
cursor: pointer;
11781216
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
1 /* This CSS file holds the default implementations for all of fossil's
2 CSS classes. When /style.css is requested, the rules in this file
3 are emitted first, followed by (1) page-specific CSS (if any) and
4 (2) skin-specific CSS.
5 */
 
 
 
6 div.sidebox {
7 float: right;
8 background-color: white;
9 border-width: medium;
10 border-style: double;
@@ -54,10 +57,13 @@
54 border-width: 0;
55 }
56 span.timelineLeaf {
57 font-weight: bold;
58 }
 
 
 
59 span.timelineHistDsp {
60 font-weight: bold;
61 }
62 td.timelineTime {
63 vertical-align: top;
@@ -557,10 +563,11 @@
557 table.diff {
558 width: 100%;
559 border-spacing: 0;
560 border-radius: 5px;
561 border: 1px solid black;
 
562 font-size: 80%;
563 }
564 table.diff td.diffln{
565 padding: 0;
566 }
@@ -748,10 +755,22 @@
748 border-bottom: 3px solid gold;
749 }
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-ci .file-change-line,
757 body.cpage-vdiff .file-change-line {
@@ -1136,19 +1155,40 @@
1136 white-space: nowrap;
1137 }
1138 label[for] {
1139 cursor: pointer;
1140 }
1141 .copy-button {
1142 display: inline-block;
 
 
1143 width: 14px;
1144 height: 14px;
1145 /*Note: .24em is slightly smaller than the average width of a normal space.*/
1146 margin: -2px .24em 0 0;
1147 padding: 0;
1148 border: 0;
 
 
 
1149 vertical-align: middle;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1150 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
1151 viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
1152 d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
1153 d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
1154 d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1159,19 +1199,17 @@
1159 d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
1160 background-repeat: no-repeat;
1161 background-position: center;
1162 cursor: pointer;
1163 }
1164 .copy-button.disabled {
 
 
 
1165 filter: grayscale(1);
1166 opacity: 0.4;
1167 }
1168 .copy-button-flipped {
1169 /*Note: .16em is suitable for element grouping.*/
1170 margin-left: .16em;
1171 margin-right: 0;
1172 }
1173 .nobr {
1174 white-space: nowrap;
1175 }
1176 .accordion {
1177 cursor: pointer;
1178
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
1 /* This CSS file holds the default implementations for all of fossil's
2 CSS classes. When /style.css is requested, the rules in this file
3 are emitted first, followed by (1) page-specific CSS (if any) and
4 (2) skin-specific CSS.
5 */
6 body {
7 z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
8 }
9 div.sidebox {
10 float: right;
11 background-color: white;
12 border-width: medium;
13 border-style: double;
@@ -54,10 +57,13 @@
57 border-width: 0;
58 }
59 span.timelineLeaf {
60 font-weight: bold;
61 }
62 span.timelineHash {
63 font-weight: bold;
64 }
65 span.timelineHistDsp {
66 font-weight: bold;
67 }
68 td.timelineTime {
69 vertical-align: top;
@@ -557,10 +563,11 @@
563 table.diff {
564 width: 100%;
565 border-spacing: 0;
566 border-radius: 5px;
567 border: 1px solid black;
568 overflow: hidden; /* Prevent background from overlapping rounded borders. */
569 font-size: 80%;
570 }
571 table.diff td.diffln{
572 padding: 0;
573 }
@@ -748,10 +755,22 @@
755 border-bottom: 3px solid gold;
756 }
757 body.tkt div.content ol.tkt-changes > li:target > ol {
758 border-left: 1px solid gold;
759 }
760 body.tkt .tktCommentArea {
761 display: flex;
762 flex-direction: column;
763 }
764 body.tkt .newest-first-controls {
765 display: flex;
766 flex-direction: row;
767 flex-wrap: nowrap;
768 }
769 body.tkt .tktCommentArea.reverse {
770 flex-direction: column-reverse;
771 }
772 body.cpage-ckout .file-change-line,
773 body.cpage-info .file-change-line,
774 body.cpage-vinfo .file-change-line,
775 body.cpage-ci .file-change-line,
776 body.cpage-vdiff .file-change-line {
@@ -1136,19 +1155,40 @@
1155 white-space: nowrap;
1156 }
1157 label[for] {
1158 cursor: pointer;
1159 }
1160 button.copy-button,
1161 button.copy-button:hover,
1162 button.copy-button:focus,
1163 button.copy-button:active {
1164 width: 14px;
1165 height: 14px;
1166 /*Note: .24em is slightly smaller than the average width of a normal space.*/
1167 margin: -2px .24em 0 0;
1168 padding: 0;
1169 border: 0;
1170 outline: 0;
1171 background: none;
1172 font-size: inherit; /* Required for horizontal spacing. */
1173 vertical-align: middle;
1174 user-select: none;
1175 cursor: pointer;
1176 }
1177 button.copy-button-flipped,
1178 button.copy-button-flipped:hover,
1179 button.copy-button-flipped:focus,
1180 button.copy-button-flipped:active {
1181 margin: -2px 0 0 .24em;
1182 }
1183 button.copy-button span {
1184 display: block;
1185 width: 100%;
1186 height: 100%;
1187 margin: 0;
1188 padding: 0;
1189 border: 0;
1190 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
1191 viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
1192 d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
1193 d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
1194 d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1159,19 +1199,17 @@
1199 d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
1200 background-repeat: no-repeat;
1201 background-position: center;
1202 cursor: pointer;
1203 }
1204 button.copy-button:enabled:active span {
1205 background-size: 90%;
1206 }
1207 button.copy-button:disabled span {
1208 filter: grayscale(1);
1209 opacity: 0.4;
1210 }
 
 
 
 
 
1211 .nobr {
1212 white-space: nowrap;
1213 }
1214 .accordion {
1215 cursor: pointer;
1216
+18 -3
--- src/deltafunc.c
+++ src/deltafunc.c
@@ -251,10 +251,11 @@
251251
if( rc==SQLITE_OK ){
252252
pNew = sqlite3_malloc64( sizeof(*pNew) );
253253
*ppVtab = (sqlite3_vtab*)pNew;
254254
if( pNew==0 ) return SQLITE_NOMEM;
255255
memset(pNew, 0, sizeof(*pNew));
256
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
256257
}
257258
return rc;
258259
}
259260
260261
/*
@@ -296,15 +297,25 @@
296297
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
297298
const char *z;
298299
int i = 0;
299300
300301
pCur->iCursor = pCur->iNext;
302
+ if( pCur->iCursor >= pCur->nDelta ){
303
+ pCur->eOp = DELTAPARSE_OP_ERROR;
304
+ pCur->iNext = pCur->nDelta;
305
+ return SQLITE_OK;
306
+ }
301307
z = pCur->aDelta + pCur->iCursor;
302308
pCur->a1 = deltaGetInt(&z, &i);
303309
switch( z[0] ){
304310
case '@': {
305311
z++;
312
+ if( pCur->iNext>=pCur->nDelta ){
313
+ pCur->eOp = DELTAPARSE_OP_ERROR;
314
+ pCur->iNext = pCur->nDelta;
315
+ break;
316
+ }
306317
pCur->a2 = deltaGetInt(&z, &i);
307318
pCur->eOp = DELTAPARSE_OP_COPY;
308319
pCur->iNext = (int)(&z[1] - pCur->aDelta);
309320
break;
310321
}
@@ -354,12 +365,16 @@
354365
}
355366
case DELTAPARSEVTAB_A2: {
356367
if( pCur->eOp==DELTAPARSE_OP_COPY ){
357368
sqlite3_result_int(ctx, pCur->a2);
358369
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
359
- sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
360
- SQLITE_TRANSIENT);
370
+ if( pCur->a2 + pCur->a1 > pCur->nDelta ){
371
+ sqlite3_result_zeroblob(ctx, pCur->a1);
372
+ }else{
373
+ sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
374
+ SQLITE_TRANSIENT);
375
+ }
361376
}
362377
break;
363378
}
364379
case DELTAPARSEVTAB_DELTA: {
365380
sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
@@ -383,11 +398,11 @@
383398
** Return TRUE if the cursor has been moved off of the last
384399
** row of output.
385400
*/
386401
static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
387402
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
388
- return pCur->eOp==DELTAPARSE_OP_EOF;
403
+ return pCur->eOp==DELTAPARSE_OP_EOF || pCur->iCursor>=pCur->nDelta;
389404
}
390405
391406
/*
392407
** This method is called to "rewind" the deltaparsevtab_cursor object back
393408
** to the first row of output. This method is always called at least
394409
--- src/deltafunc.c
+++ src/deltafunc.c
@@ -251,10 +251,11 @@
251 if( rc==SQLITE_OK ){
252 pNew = sqlite3_malloc64( sizeof(*pNew) );
253 *ppVtab = (sqlite3_vtab*)pNew;
254 if( pNew==0 ) return SQLITE_NOMEM;
255 memset(pNew, 0, sizeof(*pNew));
 
256 }
257 return rc;
258 }
259
260 /*
@@ -296,15 +297,25 @@
296 deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
297 const char *z;
298 int i = 0;
299
300 pCur->iCursor = pCur->iNext;
 
 
 
 
 
301 z = pCur->aDelta + pCur->iCursor;
302 pCur->a1 = deltaGetInt(&z, &i);
303 switch( z[0] ){
304 case '@': {
305 z++;
 
 
 
 
 
306 pCur->a2 = deltaGetInt(&z, &i);
307 pCur->eOp = DELTAPARSE_OP_COPY;
308 pCur->iNext = (int)(&z[1] - pCur->aDelta);
309 break;
310 }
@@ -354,12 +365,16 @@
354 }
355 case DELTAPARSEVTAB_A2: {
356 if( pCur->eOp==DELTAPARSE_OP_COPY ){
357 sqlite3_result_int(ctx, pCur->a2);
358 }else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
359 sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
360 SQLITE_TRANSIENT);
 
 
 
 
361 }
362 break;
363 }
364 case DELTAPARSEVTAB_DELTA: {
365 sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
@@ -383,11 +398,11 @@
383 ** Return TRUE if the cursor has been moved off of the last
384 ** row of output.
385 */
386 static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
387 deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
388 return pCur->eOp==DELTAPARSE_OP_EOF;
389 }
390
391 /*
392 ** This method is called to "rewind" the deltaparsevtab_cursor object back
393 ** to the first row of output. This method is always called at least
394
--- src/deltafunc.c
+++ src/deltafunc.c
@@ -251,10 +251,11 @@
251 if( rc==SQLITE_OK ){
252 pNew = sqlite3_malloc64( sizeof(*pNew) );
253 *ppVtab = (sqlite3_vtab*)pNew;
254 if( pNew==0 ) return SQLITE_NOMEM;
255 memset(pNew, 0, sizeof(*pNew));
256 sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
257 }
258 return rc;
259 }
260
261 /*
@@ -296,15 +297,25 @@
297 deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
298 const char *z;
299 int i = 0;
300
301 pCur->iCursor = pCur->iNext;
302 if( pCur->iCursor >= pCur->nDelta ){
303 pCur->eOp = DELTAPARSE_OP_ERROR;
304 pCur->iNext = pCur->nDelta;
305 return SQLITE_OK;
306 }
307 z = pCur->aDelta + pCur->iCursor;
308 pCur->a1 = deltaGetInt(&z, &i);
309 switch( z[0] ){
310 case '@': {
311 z++;
312 if( pCur->iNext>=pCur->nDelta ){
313 pCur->eOp = DELTAPARSE_OP_ERROR;
314 pCur->iNext = pCur->nDelta;
315 break;
316 }
317 pCur->a2 = deltaGetInt(&z, &i);
318 pCur->eOp = DELTAPARSE_OP_COPY;
319 pCur->iNext = (int)(&z[1] - pCur->aDelta);
320 break;
321 }
@@ -354,12 +365,16 @@
365 }
366 case DELTAPARSEVTAB_A2: {
367 if( pCur->eOp==DELTAPARSE_OP_COPY ){
368 sqlite3_result_int(ctx, pCur->a2);
369 }else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
370 if( pCur->a2 + pCur->a1 > pCur->nDelta ){
371 sqlite3_result_zeroblob(ctx, pCur->a1);
372 }else{
373 sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
374 SQLITE_TRANSIENT);
375 }
376 }
377 break;
378 }
379 case DELTAPARSEVTAB_DELTA: {
380 sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
@@ -383,11 +398,11 @@
398 ** Return TRUE if the cursor has been moved off of the last
399 ** row of output.
400 */
401 static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
402 deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
403 return pCur->eOp==DELTAPARSE_OP_EOF || pCur->iCursor>=pCur->nDelta;
404 }
405
406 /*
407 ** This method is called to "rewind" the deltaparsevtab_cursor object back
408 ** to the first row of output. This method is always called at least
409
+19 -13
--- src/diff.c
+++ src/diff.c
@@ -3085,10 +3085,11 @@
30853085
Blob *pOut, /* Write diff here if not NULL */
30863086
DiffConfig *pCfg /* Configuration options */
30873087
){
30883088
int ignoreWs; /* Ignore whitespace */
30893089
DContext c;
3090
+ int nDel = 0, nIns = 0;
30903091
30913092
if( pCfg->diffFlags & DIFF_INVERT ){
30923093
Blob *pTemp = pA_Blob;
30933094
pA_Blob = pB_Blob;
30943095
pB_Blob = pTemp;
@@ -3163,22 +3164,27 @@
31633164
c.aEdit[i+1] = sum;
31643165
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
31653166
c.aEdit[i+2] = sum;
31663167
}
31673168
}
3169
+
3170
+ if( pCfg->diffFlags & DIFF_NUMSTAT ){
3171
+ int i;
3172
+ for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3173
+ nDel += c.aEdit[i+1];
3174
+ nIns += c.aEdit[i+2];
3175
+ }
3176
+ g.diffCnt[1] += nIns;
3177
+ g.diffCnt[2] += nDel;
3178
+ if( nIns+nDel ){
3179
+ g.diffCnt[0]++;
3180
+ }
3181
+ }
31683182
31693183
if( pOut ){
3170
- if( pCfg->diffFlags & DIFF_NUMSTAT ){
3171
- int nDel = 0, nIns = 0, i;
3172
- for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3173
- nDel += c.aEdit[i+1];
3174
- nIns += c.aEdit[i+2];
3175
- }
3176
- g.diffCnt[1] += nIns;
3177
- g.diffCnt[2] += nDel;
3184
+ if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
31783185
if( nIns+nDel ){
3179
- g.diffCnt[0]++;
31803186
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
31813187
blob_appendf(pOut, "%10d %10d", nIns, nDel);
31823188
}
31833189
}
31843190
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3380,11 +3386,11 @@
33803386
find_option("i",0,0);
33813387
find_option("v",0,0);
33823388
diff_options(&DCfg, 0, 0);
33833389
zRe = find_option("regexp","e",1);
33843390
if( zRe ){
3385
- const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3391
+ const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
33863392
if( zErr ) fossil_fatal("regex error: %s", zErr);
33873393
}
33883394
verify_all_options();
33893395
if( g.argc!=4 ) usage("FILE1 FILE2");
33903396
blob_zero(&out);
@@ -3423,11 +3429,11 @@
34233429
find_option("i",0,0);
34243430
find_option("v",0,0);
34253431
diff_options(&DCfg, 0, 0);
34263432
zRe = find_option("regexp","e",1);
34273433
if( zRe ){
3428
- const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3434
+ const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
34293435
if( zErr ) fossil_fatal("regex error: %s", zErr);
34303436
}
34313437
db_find_and_open_repository(0, 0);
34323438
verify_all_options();
34333439
if( g.argc!=4 ) usage("HASH1 HASH2");
@@ -3781,12 +3787,12 @@
37813787
unsigned clr1, clr2, clr;
37823788
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
37833789
37843790
/* Gather query parameters */
37853791
login_check_credentials();
3786
- if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; }
3787
- if( exclude_spiders(0) ) return;
3792
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3793
+ if( robot_restrict("annotate") ) return;
37883794
fossil_nice_default();
37893795
zFilename = P("filename");
37903796
zRevision = PD("checkin",0);
37913797
zOrigin = P("origin");
37923798
zLimit = P("limit");
37933799
--- src/diff.c
+++ src/diff.c
@@ -3085,10 +3085,11 @@
3085 Blob *pOut, /* Write diff here if not NULL */
3086 DiffConfig *pCfg /* Configuration options */
3087 ){
3088 int ignoreWs; /* Ignore whitespace */
3089 DContext c;
 
3090
3091 if( pCfg->diffFlags & DIFF_INVERT ){
3092 Blob *pTemp = pA_Blob;
3093 pA_Blob = pB_Blob;
3094 pB_Blob = pTemp;
@@ -3163,22 +3164,27 @@
3163 c.aEdit[i+1] = sum;
3164 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3165 c.aEdit[i+2] = sum;
3166 }
3167 }
 
 
 
 
 
 
 
 
 
 
 
 
 
3168
3169 if( pOut ){
3170 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3171 int nDel = 0, nIns = 0, i;
3172 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3173 nDel += c.aEdit[i+1];
3174 nIns += c.aEdit[i+2];
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) ){
@@ -3380,11 +3386,11 @@
3380 find_option("i",0,0);
3381 find_option("v",0,0);
3382 diff_options(&DCfg, 0, 0);
3383 zRe = find_option("regexp","e",1);
3384 if( zRe ){
3385 const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3386 if( zErr ) fossil_fatal("regex error: %s", zErr);
3387 }
3388 verify_all_options();
3389 if( g.argc!=4 ) usage("FILE1 FILE2");
3390 blob_zero(&out);
@@ -3423,11 +3429,11 @@
3423 find_option("i",0,0);
3424 find_option("v",0,0);
3425 diff_options(&DCfg, 0, 0);
3426 zRe = find_option("regexp","e",1);
3427 if( zRe ){
3428 const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3429 if( zErr ) fossil_fatal("regex error: %s", zErr);
3430 }
3431 db_find_and_open_repository(0, 0);
3432 verify_all_options();
3433 if( g.argc!=4 ) usage("HASH1 HASH2");
@@ -3781,12 +3787,12 @@
3781 unsigned clr1, clr2, clr;
3782 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3783
3784 /* Gather query parameters */
3785 login_check_credentials();
3786 if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; }
3787 if( exclude_spiders(0) ) return;
3788 fossil_nice_default();
3789 zFilename = P("filename");
3790 zRevision = PD("checkin",0);
3791 zOrigin = P("origin");
3792 zLimit = P("limit");
3793
--- src/diff.c
+++ src/diff.c
@@ -3085,10 +3085,11 @@
3085 Blob *pOut, /* Write diff here if not NULL */
3086 DiffConfig *pCfg /* Configuration options */
3087 ){
3088 int ignoreWs; /* Ignore whitespace */
3089 DContext c;
3090 int nDel = 0, nIns = 0;
3091
3092 if( pCfg->diffFlags & DIFF_INVERT ){
3093 Blob *pTemp = pA_Blob;
3094 pA_Blob = pB_Blob;
3095 pB_Blob = pTemp;
@@ -3163,22 +3164,27 @@
3164 c.aEdit[i+1] = sum;
3165 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3166 c.aEdit[i+2] = sum;
3167 }
3168 }
3169
3170 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3171 int i;
3172 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3173 nDel += c.aEdit[i+1];
3174 nIns += c.aEdit[i+2];
3175 }
3176 g.diffCnt[1] += nIns;
3177 g.diffCnt[2] += nDel;
3178 if( nIns+nDel ){
3179 g.diffCnt[0]++;
3180 }
3181 }
3182
3183 if( pOut ){
3184 if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
 
 
 
 
 
 
 
3185 if( nIns+nDel ){
 
3186 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3187 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3188 }
3189 }
3190 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3380,11 +3386,11 @@
3386 find_option("i",0,0);
3387 find_option("v",0,0);
3388 diff_options(&DCfg, 0, 0);
3389 zRe = find_option("regexp","e",1);
3390 if( zRe ){
3391 const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
3392 if( zErr ) fossil_fatal("regex error: %s", zErr);
3393 }
3394 verify_all_options();
3395 if( g.argc!=4 ) usage("FILE1 FILE2");
3396 blob_zero(&out);
@@ -3423,11 +3429,11 @@
3429 find_option("i",0,0);
3430 find_option("v",0,0);
3431 diff_options(&DCfg, 0, 0);
3432 zRe = find_option("regexp","e",1);
3433 if( zRe ){
3434 const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
3435 if( zErr ) fossil_fatal("regex error: %s", zErr);
3436 }
3437 db_find_and_open_repository(0, 0);
3438 verify_all_options();
3439 if( g.argc!=4 ) usage("HASH1 HASH2");
@@ -3781,12 +3787,12 @@
3787 unsigned clr1, clr2, clr;
3788 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3789
3790 /* Gather query parameters */
3791 login_check_credentials();
3792 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3793 if( robot_restrict("annotate") ) return;
3794 fossil_nice_default();
3795 zFilename = P("filename");
3796 zRevision = PD("checkin",0);
3797 zOrigin = P("origin");
3798 zLimit = P("limit");
3799
+1 -1
--- src/diff.tcl
+++ src/diff.tcl
@@ -281,12 +281,12 @@
281281
if {$type ne "txt"} {
282282
$c config -width $widths($type)
283283
}
284284
$c config -state disabled
285285
}
286
+ .wfiles.lb config -height $nDiffs
286287
if {$nDiffs <= [.wfiles.lb cget -height]} {
287
- .wfiles.lb config -height $nDiffs
288288
grid remove .wfiles.sb
289289
}
290290
291291
return $nDiffs
292292
}
293293
--- src/diff.tcl
+++ src/diff.tcl
@@ -281,12 +281,12 @@
281 if {$type ne "txt"} {
282 $c config -width $widths($type)
283 }
284 $c config -state disabled
285 }
 
286 if {$nDiffs <= [.wfiles.lb cget -height]} {
287 .wfiles.lb config -height $nDiffs
288 grid remove .wfiles.sb
289 }
290
291 return $nDiffs
292 }
293
--- src/diff.tcl
+++ src/diff.tcl
@@ -281,12 +281,12 @@
281 if {$type ne "txt"} {
282 $c config -width $widths($type)
283 }
284 $c config -state disabled
285 }
286 .wfiles.lb config -height $nDiffs
287 if {$nDiffs <= [.wfiles.lb cget -height]} {
 
288 grid remove .wfiles.sb
289 }
290
291 return $nDiffs
292 }
293
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1522,10 +1522,11 @@
15221522
DiffConfig DCfg;
15231523
cgi_check_for_malice();
15241524
login_check_credentials();
15251525
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
15261526
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1527
+ if( robot_restrict("diff") ) return;
15271528
15281529
fossil_nice_default();
15291530
cgi_set_content_type("text/plain");
15301531
diff_config_init(&DCfg, DIFF_VERBOSE);
15311532
diff_two_versions(zFrom, zTo, &DCfg, 0);
15321533
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1522,10 +1522,11 @@
1522 DiffConfig DCfg;
1523 cgi_check_for_malice();
1524 login_check_credentials();
1525 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1526 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
 
1527
1528 fossil_nice_default();
1529 cgi_set_content_type("text/plain");
1530 diff_config_init(&DCfg, DIFF_VERBOSE);
1531 diff_two_versions(zFrom, zTo, &DCfg, 0);
1532
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1522,10 +1522,11 @@
1522 DiffConfig DCfg;
1523 cgi_check_for_malice();
1524 login_check_credentials();
1525 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1526 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1527 if( robot_restrict("diff") ) return;
1528
1529 fossil_nice_default();
1530 cgi_set_content_type("text/plain");
1531 diff_config_init(&DCfg, DIFF_VERBOSE);
1532 diff_two_versions(zFrom, zTo, &DCfg, 0);
1533
+29 -15
--- src/dispatch.c
+++ src/dispatch.c
@@ -59,11 +59,11 @@
5959
#define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */
6060
/**************************************************************************/
6161
6262
/* Values for the 2nd parameter to dispatch_name_search() */
6363
#define CMDFLAG_ANY 0x0038 /* Match anything */
64
-#define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */
64
+#define CMDFLAG_PREFIX 0x0200 /* Prefix match is OK */
6565
6666
#endif /* INTERFACE */
6767
6868
/*
6969
** The page_index.h file contains the definition for aCommand[] - an array
@@ -275,11 +275,11 @@
275275
char c = z[i];
276276
if( c=='[' && (j = help_is_link(z+i, n-i))>0 ){
277277
if( i ) blob_append(pOut, z, i);
278278
z += i+2;
279279
n -= i+2;
280
- blob_appendf(pOut, "<a href='%R/help?cmd=%.*s'>%.*s</a>",
280
+ blob_appendf(pOut, "<a href='%R/help/%.*s'>%.*s</a>",
281281
j-3, z, j-3, z);
282282
z += j-1;
283283
n -= j-1;
284284
i = 0;
285285
}else if( c=='%' && n-i>=7 && strncmp(z+i,"%fossil",7)==0 ){
@@ -835,18 +835,27 @@
835835
return 0;
836836
}
837837
838838
/*
839839
** WEBPAGE: help
840
-** URL: /help?name=CMD
840
+** URL: /help/CMD or /help/www/PAGE
841841
**
842
-** Show the built-in help text for CMD. CMD can be a command-line interface
843
-** command or a page name from the web interface or a setting.
842
+** Show the built-in help text for CMD or PAGE. CMD can be a command-line
843
+** interface command or a setting name. PAGE is the name of a
844
+** web interface. /help//PAGE also works if the double-/ makes it through
845
+** the main web server.
846
+**
844847
** Query parameters:
845848
**
846849
** name=CMD Show help for CMD where CMD is a command name or
847
-** webpage name or setting name.
850
+** or setting name. If CMD beings with "/" it is
851
+** interpreted as a PAGE name.
852
+**
853
+** name=www/PAGE Show help for web page PAGE.
854
+**
855
+** name=/PAGE The initial "www/" on web-page help can be abbreviated as
856
+** just "/"
848857
**
849858
** plaintext Show the help within <pre>...</pre>, as if it were
850859
** displayed using the "fossil help" command.
851860
**
852861
** raw Show the raw help text without any formatting.
@@ -863,10 +872,15 @@
863872
864873
style_set_current_feature("tkt");
865874
style_submenu_element("Topic-List", "%R/help");
866875
if( search_restrict(SRCH_HELP)!=0 ){
867876
style_submenu_element("Search","%R/search?y=h");
877
+ }
878
+ if( strncmp(zCmd,"www/",4)==0 && zCmd[4]!=0 ){
879
+ /* Use https://domain/fossil/help/www/timeline or similar with the "www"
880
+ ** intermediate tag to view web-page documentation. */
881
+ zCmd += 3;
868882
}
869883
rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
870884
if( pCmd ){
871885
style_header("Help: %s", pCmd->zName);
872886
}else{
@@ -935,11 +949,11 @@
935949
const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
936950
if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
937951
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
938952
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
939953
else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue;
940
- @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
954
+ @ <li><a href="%R/help/%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
941955
/* Output aliases */
942956
if( occHelp[aCommand[i].iHelp] > 1 ){
943957
int j;
944958
int aliases[MX_HELP_DUP], nAliases=0;
945959
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
@@ -952,11 +966,11 @@
952966
}
953967
if( nAliases>0 ){
954968
int k;
955969
@(\
956970
for(k=0; k<nAliases; k++){
957
- @<a href="%R/help?cmd=%s(aCommand[aliases[k]].zName)">\
971
+ @<a href="%R/help/%s(aCommand[aliases[k]].zName)">\
958972
@%s(aCommand[aliases[k]].zName)</a>%s((k<nAliases-1)?", ":"")\
959973
}
960974
@)\
961975
}
962976
}
@@ -972,11 +986,11 @@
972986
for(i=0; i<MX_COMMAND; i++){
973987
const char *z = aCommand[i].zName;
974988
if( '/'!=*z ) continue;
975989
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
976990
if( aCommand[i].zHelp[0] ){
977
- @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
991
+ @ <li><a href="%R/help/www%s(z)">%s(z+1)</a></li>
978992
}else{
979993
@ <li>%s(z+1)</li>
980994
}
981995
}
982996
@ </ul></div>
@@ -988,11 +1002,11 @@
9881002
for(i=0; i<MX_COMMAND; i++){
9891003
const char *z = aCommand[i].zName;
9901004
if( strncmp(z,"test",4)!=0 ) continue;
9911005
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
9921006
if( aCommand[i].zHelp[0] ){
993
- @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
1007
+ @ <li><a href="%R/help/%s(z)">%s(z)</a></li>
9941008
}else{
9951009
@ <li>%s(z)</li>
9961010
}
9971011
}
9981012
@ </ul></div>
@@ -1004,11 +1018,11 @@
10041018
for(i=0; i<MX_COMMAND; i++){
10051019
const char *z = aCommand[i].zName;
10061020
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
10071021
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
10081022
if( aCommand[i].zHelp[0] ){
1009
- @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
1023
+ @ <li><a href="%R/help/%s(z)">%s(z)</a></li>
10101024
}else{
10111025
@ <li>%s(z)</li>
10121026
}
10131027
}
10141028
@ </ul></div>
@@ -1155,11 +1169,11 @@
11551169
}else{
11561170
zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
11571171
}
11581172
fossil_free(zQTop);
11591173
fossil_free(zQSub);
1160
- re_compile(&pRe, zPattern, 0);
1174
+ fossil_re_compile(&pRe, zPattern, 0);
11611175
fossil_free(zPattern);
11621176
blob_init(&in, z, -1);
11631177
while( blob_line(&in, &line) ){
11641178
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
11651179
int atStart = 1;
@@ -1252,13 +1266,13 @@
12521266
ReCompiled *pRe = 0;
12531267
Blob in, line;
12541268
int n = 0;
12551269
12561270
if( bAbbrevSubcmd ){
1257
- re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
1271
+ fossil_re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
12581272
}else{
1259
- re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
1273
+ fossil_re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
12601274
}
12611275
blob_init(&in, z, -1);
12621276
while( blob_line(&in, &line) ){
12631277
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
12641278
simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
@@ -1285,11 +1299,11 @@
12851299
int n = 0;
12861300
int bSubsectionSeen = 0;
12871301
12881302
blob_init(&txt, z, -1);
12891303
blob_init(&subsection, 0, 0);
1290
- re_compile(&pRe, "^ +-.* ", 0);
1304
+ fossil_re_compile(&pRe, "^ +-.* ", 0);
12911305
while( blob_line(&txt, &line) ){
12921306
int len = blob_size(&line);
12931307
unsigned char *zLine = (unsigned char *)blob_buffer(&line);
12941308
if( re_match(pRe, zLine, len) ){
12951309
if( blob_size(&subsection) ){
12961310
--- src/dispatch.c
+++ src/dispatch.c
@@ -59,11 +59,11 @@
59 #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */
60 /**************************************************************************/
61
62 /* Values for the 2nd parameter to dispatch_name_search() */
63 #define CMDFLAG_ANY 0x0038 /* Match anything */
64 #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */
65
66 #endif /* INTERFACE */
67
68 /*
69 ** The page_index.h file contains the definition for aCommand[] - an array
@@ -275,11 +275,11 @@
275 char c = z[i];
276 if( c=='[' && (j = help_is_link(z+i, n-i))>0 ){
277 if( i ) blob_append(pOut, z, i);
278 z += i+2;
279 n -= i+2;
280 blob_appendf(pOut, "<a href='%R/help?cmd=%.*s'>%.*s</a>",
281 j-3, z, j-3, z);
282 z += j-1;
283 n -= j-1;
284 i = 0;
285 }else if( c=='%' && n-i>=7 && strncmp(z+i,"%fossil",7)==0 ){
@@ -835,18 +835,27 @@
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
843 ** command or a page name from the web interface or a setting.
 
 
 
844 ** Query parameters:
845 **
846 ** name=CMD Show help for CMD where CMD is a command name or
847 ** webpage name or setting name.
 
 
 
 
 
 
848 **
849 ** plaintext Show the help within <pre>...</pre>, as if it were
850 ** displayed using the "fossil help" command.
851 **
852 ** raw Show the raw help text without any formatting.
@@ -863,10 +872,15 @@
863
864 style_set_current_feature("tkt");
865 style_submenu_element("Topic-List", "%R/help");
866 if( search_restrict(SRCH_HELP)!=0 ){
867 style_submenu_element("Search","%R/search?y=h");
 
 
 
 
 
868 }
869 rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
870 if( pCmd ){
871 style_header("Help: %s", pCmd->zName);
872 }else{
@@ -935,11 +949,11 @@
935 const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
936 if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
937 if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
938 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
939 else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue;
940 @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
941 /* Output aliases */
942 if( occHelp[aCommand[i].iHelp] > 1 ){
943 int j;
944 int aliases[MX_HELP_DUP], nAliases=0;
945 for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
@@ -952,11 +966,11 @@
952 }
953 if( nAliases>0 ){
954 int k;
955 @(\
956 for(k=0; k<nAliases; k++){
957 @<a href="%R/help?cmd=%s(aCommand[aliases[k]].zName)">\
958 @%s(aCommand[aliases[k]].zName)</a>%s((k<nAliases-1)?", ":"")\
959 }
960 @)\
961 }
962 }
@@ -972,11 +986,11 @@
972 for(i=0; i<MX_COMMAND; i++){
973 const char *z = aCommand[i].zName;
974 if( '/'!=*z ) continue;
975 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
976 if( aCommand[i].zHelp[0] ){
977 @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
978 }else{
979 @ <li>%s(z+1)</li>
980 }
981 }
982 @ </ul></div>
@@ -988,11 +1002,11 @@
988 for(i=0; i<MX_COMMAND; i++){
989 const char *z = aCommand[i].zName;
990 if( strncmp(z,"test",4)!=0 ) continue;
991 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
992 if( aCommand[i].zHelp[0] ){
993 @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
994 }else{
995 @ <li>%s(z)</li>
996 }
997 }
998 @ </ul></div>
@@ -1004,11 +1018,11 @@
1004 for(i=0; i<MX_COMMAND; i++){
1005 const char *z = aCommand[i].zName;
1006 if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
1007 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
1008 if( aCommand[i].zHelp[0] ){
1009 @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
1010 }else{
1011 @ <li>%s(z)</li>
1012 }
1013 }
1014 @ </ul></div>
@@ -1155,11 +1169,11 @@
1155 }else{
1156 zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
1157 }
1158 fossil_free(zQTop);
1159 fossil_free(zQSub);
1160 re_compile(&pRe, zPattern, 0);
1161 fossil_free(zPattern);
1162 blob_init(&in, z, -1);
1163 while( blob_line(&in, &line) ){
1164 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
1165 int atStart = 1;
@@ -1252,13 +1266,13 @@
1252 ReCompiled *pRe = 0;
1253 Blob in, line;
1254 int n = 0;
1255
1256 if( bAbbrevSubcmd ){
1257 re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
1258 }else{
1259 re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
1260 }
1261 blob_init(&in, z, -1);
1262 while( blob_line(&in, &line) ){
1263 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
1264 simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
@@ -1285,11 +1299,11 @@
1285 int n = 0;
1286 int bSubsectionSeen = 0;
1287
1288 blob_init(&txt, z, -1);
1289 blob_init(&subsection, 0, 0);
1290 re_compile(&pRe, "^ +-.* ", 0);
1291 while( blob_line(&txt, &line) ){
1292 int len = blob_size(&line);
1293 unsigned char *zLine = (unsigned char *)blob_buffer(&line);
1294 if( re_match(pRe, zLine, len) ){
1295 if( blob_size(&subsection) ){
1296
--- src/dispatch.c
+++ src/dispatch.c
@@ -59,11 +59,11 @@
59 #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */
60 /**************************************************************************/
61
62 /* Values for the 2nd parameter to dispatch_name_search() */
63 #define CMDFLAG_ANY 0x0038 /* Match anything */
64 #define CMDFLAG_PREFIX 0x0200 /* Prefix match is OK */
65
66 #endif /* INTERFACE */
67
68 /*
69 ** The page_index.h file contains the definition for aCommand[] - an array
@@ -275,11 +275,11 @@
275 char c = z[i];
276 if( c=='[' && (j = help_is_link(z+i, n-i))>0 ){
277 if( i ) blob_append(pOut, z, i);
278 z += i+2;
279 n -= i+2;
280 blob_appendf(pOut, "<a href='%R/help/%.*s'>%.*s</a>",
281 j-3, z, j-3, z);
282 z += j-1;
283 n -= j-1;
284 i = 0;
285 }else if( c=='%' && n-i>=7 && strncmp(z+i,"%fossil",7)==0 ){
@@ -835,18 +835,27 @@
835 return 0;
836 }
837
838 /*
839 ** WEBPAGE: help
840 ** URL: /help/CMD or /help/www/PAGE
841 **
842 ** Show the built-in help text for CMD or PAGE. CMD can be a command-line
843 ** interface command or a setting name. PAGE is the name of a
844 ** web interface. /help//PAGE also works if the double-/ makes it through
845 ** the main web server.
846 **
847 ** Query parameters:
848 **
849 ** name=CMD Show help for CMD where CMD is a command name or
850 ** or setting name. If CMD beings with "/" it is
851 ** interpreted as a PAGE name.
852 **
853 ** name=www/PAGE Show help for web page PAGE.
854 **
855 ** name=/PAGE The initial "www/" on web-page help can be abbreviated as
856 ** just "/"
857 **
858 ** plaintext Show the help within <pre>...</pre>, as if it were
859 ** displayed using the "fossil help" command.
860 **
861 ** raw Show the raw help text without any formatting.
@@ -863,10 +872,15 @@
872
873 style_set_current_feature("tkt");
874 style_submenu_element("Topic-List", "%R/help");
875 if( search_restrict(SRCH_HELP)!=0 ){
876 style_submenu_element("Search","%R/search?y=h");
877 }
878 if( strncmp(zCmd,"www/",4)==0 && zCmd[4]!=0 ){
879 /* Use https://domain/fossil/help/www/timeline or similar with the "www"
880 ** intermediate tag to view web-page documentation. */
881 zCmd += 3;
882 }
883 rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
884 if( pCmd ){
885 style_header("Help: %s", pCmd->zName);
886 }else{
@@ -935,11 +949,11 @@
949 const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
950 if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
951 if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
952 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
953 else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue;
954 @ <li><a href="%R/help/%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
955 /* Output aliases */
956 if( occHelp[aCommand[i].iHelp] > 1 ){
957 int j;
958 int aliases[MX_HELP_DUP], nAliases=0;
959 for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
@@ -952,11 +966,11 @@
966 }
967 if( nAliases>0 ){
968 int k;
969 @(\
970 for(k=0; k<nAliases; k++){
971 @<a href="%R/help/%s(aCommand[aliases[k]].zName)">\
972 @%s(aCommand[aliases[k]].zName)</a>%s((k<nAliases-1)?", ":"")\
973 }
974 @)\
975 }
976 }
@@ -972,11 +986,11 @@
986 for(i=0; i<MX_COMMAND; i++){
987 const char *z = aCommand[i].zName;
988 if( '/'!=*z ) continue;
989 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
990 if( aCommand[i].zHelp[0] ){
991 @ <li><a href="%R/help/www%s(z)">%s(z+1)</a></li>
992 }else{
993 @ <li>%s(z+1)</li>
994 }
995 }
996 @ </ul></div>
@@ -988,11 +1002,11 @@
1002 for(i=0; i<MX_COMMAND; i++){
1003 const char *z = aCommand[i].zName;
1004 if( strncmp(z,"test",4)!=0 ) continue;
1005 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
1006 if( aCommand[i].zHelp[0] ){
1007 @ <li><a href="%R/help/%s(z)">%s(z)</a></li>
1008 }else{
1009 @ <li>%s(z)</li>
1010 }
1011 }
1012 @ </ul></div>
@@ -1004,11 +1018,11 @@
1018 for(i=0; i<MX_COMMAND; i++){
1019 const char *z = aCommand[i].zName;
1020 if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
1021 else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
1022 if( aCommand[i].zHelp[0] ){
1023 @ <li><a href="%R/help/%s(z)">%s(z)</a></li>
1024 }else{
1025 @ <li>%s(z)</li>
1026 }
1027 }
1028 @ </ul></div>
@@ -1155,11 +1169,11 @@
1169 }else{
1170 zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
1171 }
1172 fossil_free(zQTop);
1173 fossil_free(zQSub);
1174 fossil_re_compile(&pRe, zPattern, 0);
1175 fossil_free(zPattern);
1176 blob_init(&in, z, -1);
1177 while( blob_line(&in, &line) ){
1178 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
1179 int atStart = 1;
@@ -1252,13 +1266,13 @@
1266 ReCompiled *pRe = 0;
1267 Blob in, line;
1268 int n = 0;
1269
1270 if( bAbbrevSubcmd ){
1271 fossil_re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
1272 }else{
1273 fossil_re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
1274 }
1275 blob_init(&in, z, -1);
1276 while( blob_line(&in, &line) ){
1277 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
1278 simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
@@ -1285,11 +1299,11 @@
1299 int n = 0;
1300 int bSubsectionSeen = 0;
1301
1302 blob_init(&txt, z, -1);
1303 blob_init(&subsection, 0, 0);
1304 fossil_re_compile(&pRe, "^ +-.* ", 0);
1305 while( blob_line(&txt, &line) ){
1306 int len = blob_size(&line);
1307 unsigned char *zLine = (unsigned char *)blob_buffer(&line);
1308 if( re_match(pRe, zLine, len) ){
1309 if( blob_size(&subsection) ){
1310
+3 -3
--- src/doc.c
+++ src/doc.c
@@ -520,20 +520,20 @@
520520
char *zCustomList = 0; /* value of the mimetypes setting */
521521
int nCustomEntries = 0; /* number of entries in the mimetypes
522522
** setting */
523523
mimetype_verify();
524524
style_header("Mimetype List");
525
- @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
525
+ @ <p>The Fossil <a href="%R/help/www/doc">/doc</a> page uses filename
526526
@ suffixes and the following tables to guess at the appropriate mimetype
527527
@ for each document. Mimetypes may be customized and overridden using
528
- @ <a href="%R/help?cmd=mimetypes">the mimetypes config setting</a>.</p>
528
+ @ <a href="%R/help/mimetypes">the mimetypes config setting</a>.</p>
529529
zCustomList = db_get("mimetypes",0);
530530
if( zCustomList!=0 ){
531531
Blob list, entry, key, val;
532532
@ <h1>Repository-specific mimetypes</h1>
533533
@ <p>The following extension-to-mimetype mappings are defined via
534
- @ the <a href="%R/help?cmd=mimetypes">mimetypes setting</a>.</p>
534
+ @ the <a href="%R/help/mimetypes">mimetypes setting</a>.</p>
535535
@ <table class='sortable mimetypetable' border=1 cellpadding=0 \
536536
@ data-column-types='tt' data-init-sort='0'>
537537
@ <thead>
538538
@ <tr><th>Suffix<th>Mimetype
539539
@ </thead>
540540
--- src/doc.c
+++ src/doc.c
@@ -520,20 +520,20 @@
520 char *zCustomList = 0; /* value of the mimetypes setting */
521 int nCustomEntries = 0; /* number of entries in the mimetypes
522 ** setting */
523 mimetype_verify();
524 style_header("Mimetype List");
525 @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
526 @ suffixes and the following tables to guess at the appropriate mimetype
527 @ for each document. Mimetypes may be customized and overridden using
528 @ <a href="%R/help?cmd=mimetypes">the mimetypes config setting</a>.</p>
529 zCustomList = db_get("mimetypes",0);
530 if( zCustomList!=0 ){
531 Blob list, entry, key, val;
532 @ <h1>Repository-specific mimetypes</h1>
533 @ <p>The following extension-to-mimetype mappings are defined via
534 @ the <a href="%R/help?cmd=mimetypes">mimetypes setting</a>.</p>
535 @ <table class='sortable mimetypetable' border=1 cellpadding=0 \
536 @ data-column-types='tt' data-init-sort='0'>
537 @ <thead>
538 @ <tr><th>Suffix<th>Mimetype
539 @ </thead>
540
--- src/doc.c
+++ src/doc.c
@@ -520,20 +520,20 @@
520 char *zCustomList = 0; /* value of the mimetypes setting */
521 int nCustomEntries = 0; /* number of entries in the mimetypes
522 ** setting */
523 mimetype_verify();
524 style_header("Mimetype List");
525 @ <p>The Fossil <a href="%R/help/www/doc">/doc</a> page uses filename
526 @ suffixes and the following tables to guess at the appropriate mimetype
527 @ for each document. Mimetypes may be customized and overridden using
528 @ <a href="%R/help/mimetypes">the mimetypes config setting</a>.</p>
529 zCustomList = db_get("mimetypes",0);
530 if( zCustomList!=0 ){
531 Blob list, entry, key, val;
532 @ <h1>Repository-specific mimetypes</h1>
533 @ <p>The following extension-to-mimetype mappings are defined via
534 @ the <a href="%R/help/mimetypes">mimetypes setting</a>.</p>
535 @ <table class='sortable mimetypetable' border=1 cellpadding=0 \
536 @ data-column-types='tt' data-init-sort='0'>
537 @ <thead>
538 @ <tr><th>Suffix<th>Mimetype
539 @ </thead>
540
+2 -1
--- src/extcgi.c
+++ src/extcgi.c
@@ -229,11 +229,12 @@
229229
zFailReason = "path does not match any file or script";
230230
goto ext_not_found;
231231
}
232232
assert( nScript>=nRoot+1 );
233233
style_set_current_page("ext/%s", &zScript[nRoot+1]);
234
- zMime = mimetype_from_name(zScript);
234
+ zMime = P("mimetype");
235
+ if( zMime==0 ) zMime = mimetype_from_name(zScript);
235236
if( zMime==0 ) zMime = "application/octet-stream";
236237
if( !file_isexe(zScript, ExtFILE) ){
237238
/* File is not executable. Must be a regular file. In that case,
238239
** disallow extra path elements */
239240
if( zPath[nScript]!=0 ){
240241
--- src/extcgi.c
+++ src/extcgi.c
@@ -229,11 +229,12 @@
229 zFailReason = "path does not match any file or script";
230 goto ext_not_found;
231 }
232 assert( nScript>=nRoot+1 );
233 style_set_current_page("ext/%s", &zScript[nRoot+1]);
234 zMime = mimetype_from_name(zScript);
 
235 if( zMime==0 ) zMime = "application/octet-stream";
236 if( !file_isexe(zScript, ExtFILE) ){
237 /* File is not executable. Must be a regular file. In that case,
238 ** disallow extra path elements */
239 if( zPath[nScript]!=0 ){
240
--- src/extcgi.c
+++ src/extcgi.c
@@ -229,11 +229,12 @@
229 zFailReason = "path does not match any file or script";
230 goto ext_not_found;
231 }
232 assert( nScript>=nRoot+1 );
233 style_set_current_page("ext/%s", &zScript[nRoot+1]);
234 zMime = P("mimetype");
235 if( zMime==0 ) zMime = mimetype_from_name(zScript);
236 if( zMime==0 ) zMime = "application/octet-stream";
237 if( !file_isexe(zScript, ExtFILE) ){
238 /* File is not executable. Must be a regular file. In that case,
239 ** disallow extra path elements */
240 if( zPath[nScript]!=0 ){
241
+18 -8
--- src/finfo.c
+++ src/finfo.c
@@ -394,10 +394,12 @@
394394
tmFlags = timeline_ss_submenu();
395395
if( tmFlags & TIMELINE_COLUMNAR ){
396396
zStyle = "Columnar";
397397
}else if( tmFlags & TIMELINE_COMPACT ){
398398
zStyle = "Compact";
399
+ }else if( tmFlags & TIMELINE_SIMPLE ){
400
+ zStyle = "Simple";
399401
}else if( tmFlags & TIMELINE_VERBOSE ){
400402
zStyle = "Verbose";
401403
}else if( tmFlags & TIMELINE_CLASSIC ){
402404
zStyle = "Classic";
403405
}else{
@@ -731,14 +733,14 @@
731733
}
732734
if( tmFlags & TIMELINE_COMPACT ){
733735
cgi_printf("<span class='clutter' id='detail-%d'>",frid);
734736
}
735737
cgi_printf("<span class='timeline%sDetail'>", zStyle);
736
- if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
738
+ if( tmFlags & TIMELINE_INLINE ) cgi_printf("(");
737739
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
738740
@ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\
739
- @ [%S(zUuid)]</a>
741
+ @ %S(zUuid)</a>
740742
if( fShowId ){
741743
int srcId = delta_source_rid(frid);
742744
if( srcId>0 ){
743745
@ id:&nbsp;%z(href("%R/deltachain/%d",frid))\
744746
@ %d(frid)&larr;%d(srcId)</a>
@@ -745,20 +747,25 @@
745747
}else{
746748
@ id:&nbsp;%z(href("%R/deltachain/%d",frid))%d(frid)</a>
747749
}
748750
}
749751
}
752
+ if( tmFlags & TIMELINE_SIMPLE ){
753
+ @ <span class='timelineEllipsis' data-id='%d(frid)' \
754
+ @ id='ellipsis-%d(frid)'>...</span>
755
+ @ <span class='clutter' id='detail-%d(frid)'>
756
+ }
750757
@ check-in:&nbsp;\
751758
hyperlink_to_version(zCkin);
752759
if( fShowId ){
753760
@ (%d(fmid))
754761
}
755762
@ user:&nbsp;\
756763
hyperlink_to_user(zUser, zDate, ",");
757764
@ branch:&nbsp;%z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
758
- if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){
759
- @ size:&nbsp;%d(szFile))
765
+ if( tmFlags & TIMELINE_INLINE ){
766
+ @ size:&nbsp;%d(szFile)
760767
}else{
761768
@ size:&nbsp;%d(szFile)
762769
}
763770
if( g.perm.Hyperlink && zUuid ){
764771
const char *z = zFName;
@@ -793,14 +800,14 @@
793800
zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFName,zCkin);
794801
@ %z(zAncLink)[ancestry]</a>
795802
}
796803
tag_private_status(frid);
797804
/* End timelineDetail */
798
- if( tmFlags & TIMELINE_COMPACT ){
799
- @ </span></span>
805
+ if( tmFlags & (TIMELINE_COMPACT|TIMELINE_SIMPLE) ){
806
+ @ </span>)</span>
800807
}else{
801
- @ </span>
808
+ @ )</span>
802809
}
803810
@ </td></tr>
804811
}
805812
db_finalize(&q);
806813
db_finalize(&qparent);
@@ -813,11 +820,14 @@
813820
@ <tr class="timelineBottom" id="btm-%d(iTableId)">\
814821
@ <td></td><td></td><td></td></tr>
815822
}
816823
}
817824
@ </table>
818
- timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
825
+ {
826
+ int tmFlags = TIMELINE_GRAPH | TIMELINE_FILEDIFF;
827
+ timeline_output_graph_javascript(pGraph, tmFlags, iTableId);
828
+ }
819829
style_finish_page();
820830
}
821831
822832
/*
823833
** WEBPAGE: mlink
824834
--- src/finfo.c
+++ src/finfo.c
@@ -394,10 +394,12 @@
394 tmFlags = timeline_ss_submenu();
395 if( tmFlags & TIMELINE_COLUMNAR ){
396 zStyle = "Columnar";
397 }else if( tmFlags & TIMELINE_COMPACT ){
398 zStyle = "Compact";
 
 
399 }else if( tmFlags & TIMELINE_VERBOSE ){
400 zStyle = "Verbose";
401 }else if( tmFlags & TIMELINE_CLASSIC ){
402 zStyle = "Classic";
403 }else{
@@ -731,14 +733,14 @@
731 }
732 if( tmFlags & TIMELINE_COMPACT ){
733 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
734 }
735 cgi_printf("<span class='timeline%sDetail'>", zStyle);
736 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
737 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
738 @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\
739 @ [%S(zUuid)]</a>
740 if( fShowId ){
741 int srcId = delta_source_rid(frid);
742 if( srcId>0 ){
743 @ id:&nbsp;%z(href("%R/deltachain/%d",frid))\
744 @ %d(frid)&larr;%d(srcId)</a>
@@ -745,20 +747,25 @@
745 }else{
746 @ id:&nbsp;%z(href("%R/deltachain/%d",frid))%d(frid)</a>
747 }
748 }
749 }
 
 
 
 
 
750 @ check-in:&nbsp;\
751 hyperlink_to_version(zCkin);
752 if( fShowId ){
753 @ (%d(fmid))
754 }
755 @ user:&nbsp;\
756 hyperlink_to_user(zUser, zDate, ",");
757 @ branch:&nbsp;%z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
758 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){
759 @ size:&nbsp;%d(szFile))
760 }else{
761 @ size:&nbsp;%d(szFile)
762 }
763 if( g.perm.Hyperlink && zUuid ){
764 const char *z = zFName;
@@ -793,14 +800,14 @@
793 zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFName,zCkin);
794 @ %z(zAncLink)[ancestry]</a>
795 }
796 tag_private_status(frid);
797 /* End timelineDetail */
798 if( tmFlags & TIMELINE_COMPACT ){
799 @ </span></span>
800 }else{
801 @ </span>
802 }
803 @ </td></tr>
804 }
805 db_finalize(&q);
806 db_finalize(&qparent);
@@ -813,11 +820,14 @@
813 @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
814 @ <td></td><td></td><td></td></tr>
815 }
816 }
817 @ </table>
818 timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
 
 
 
819 style_finish_page();
820 }
821
822 /*
823 ** WEBPAGE: mlink
824
--- src/finfo.c
+++ src/finfo.c
@@ -394,10 +394,12 @@
394 tmFlags = timeline_ss_submenu();
395 if( tmFlags & TIMELINE_COLUMNAR ){
396 zStyle = "Columnar";
397 }else if( tmFlags & TIMELINE_COMPACT ){
398 zStyle = "Compact";
399 }else if( tmFlags & TIMELINE_SIMPLE ){
400 zStyle = "Simple";
401 }else if( tmFlags & TIMELINE_VERBOSE ){
402 zStyle = "Verbose";
403 }else if( tmFlags & TIMELINE_CLASSIC ){
404 zStyle = "Classic";
405 }else{
@@ -731,14 +733,14 @@
733 }
734 if( tmFlags & TIMELINE_COMPACT ){
735 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
736 }
737 cgi_printf("<span class='timeline%sDetail'>", zStyle);
738 if( tmFlags & TIMELINE_INLINE ) cgi_printf("(");
739 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
740 @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\
741 @ %S(zUuid)</a>
742 if( fShowId ){
743 int srcId = delta_source_rid(frid);
744 if( srcId>0 ){
745 @ id:&nbsp;%z(href("%R/deltachain/%d",frid))\
746 @ %d(frid)&larr;%d(srcId)</a>
@@ -745,20 +747,25 @@
747 }else{
748 @ id:&nbsp;%z(href("%R/deltachain/%d",frid))%d(frid)</a>
749 }
750 }
751 }
752 if( tmFlags & TIMELINE_SIMPLE ){
753 @ <span class='timelineEllipsis' data-id='%d(frid)' \
754 @ id='ellipsis-%d(frid)'>...</span>
755 @ <span class='clutter' id='detail-%d(frid)'>
756 }
757 @ check-in:&nbsp;\
758 hyperlink_to_version(zCkin);
759 if( fShowId ){
760 @ (%d(fmid))
761 }
762 @ user:&nbsp;\
763 hyperlink_to_user(zUser, zDate, ",");
764 @ branch:&nbsp;%z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
765 if( tmFlags & TIMELINE_INLINE ){
766 @ size:&nbsp;%d(szFile)
767 }else{
768 @ size:&nbsp;%d(szFile)
769 }
770 if( g.perm.Hyperlink && zUuid ){
771 const char *z = zFName;
@@ -793,14 +800,14 @@
800 zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFName,zCkin);
801 @ %z(zAncLink)[ancestry]</a>
802 }
803 tag_private_status(frid);
804 /* End timelineDetail */
805 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_SIMPLE) ){
806 @ </span>)</span>
807 }else{
808 @ )</span>
809 }
810 @ </td></tr>
811 }
812 db_finalize(&q);
813 db_finalize(&qparent);
@@ -813,11 +820,14 @@
820 @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
821 @ <td></td><td></td><td></td></tr>
822 }
823 }
824 @ </table>
825 {
826 int tmFlags = TIMELINE_GRAPH | TIMELINE_FILEDIFF;
827 timeline_output_graph_javascript(pGraph, tmFlags, iTableId);
828 }
829 style_finish_page();
830 }
831
832 /*
833 ** WEBPAGE: mlink
834
+2 -2
--- src/forum.c
+++ src/forum.c
@@ -1912,18 +1912,18 @@
19121912
zQP[1] = zQP[0];
19131913
zQP[2] = 0;
19141914
if( pSetting->width==0 ){
19151915
/* Boolean setting */
19161916
@ <tr><td align="right">
1917
- @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
1917
+ @ <a href='%R/help/%h(pSetting->name)'>%h(pSetting->name)</a>:
19181918
@ </td><td>
19191919
onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0);
19201920
@ </td></tr>
19211921
}else{
19221922
/* Text value setting */
19231923
@ <tr><td align="right">
1924
- @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
1924
+ @ <a href='%R/help/%h(pSetting->name)'>%h(pSetting->name)</a>:
19251925
@ </td><td>
19261926
entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/,
19271927
pSetting->def, 0);
19281928
@ </td></tr>
19291929
}
19301930
--- src/forum.c
+++ src/forum.c
@@ -1912,18 +1912,18 @@
1912 zQP[1] = zQP[0];
1913 zQP[2] = 0;
1914 if( pSetting->width==0 ){
1915 /* Boolean setting */
1916 @ <tr><td align="right">
1917 @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
1918 @ </td><td>
1919 onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0);
1920 @ </td></tr>
1921 }else{
1922 /* Text value setting */
1923 @ <tr><td align="right">
1924 @ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
1925 @ </td><td>
1926 entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/,
1927 pSetting->def, 0);
1928 @ </td></tr>
1929 }
1930
--- src/forum.c
+++ src/forum.c
@@ -1912,18 +1912,18 @@
1912 zQP[1] = zQP[0];
1913 zQP[2] = 0;
1914 if( pSetting->width==0 ){
1915 /* Boolean setting */
1916 @ <tr><td align="right">
1917 @ <a href='%R/help/%h(pSetting->name)'>%h(pSetting->name)</a>:
1918 @ </td><td>
1919 onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0);
1920 @ </td></tr>
1921 }else{
1922 /* Text value setting */
1923 @ <tr><td align="right">
1924 @ <a href='%R/help/%h(pSetting->name)'>%h(pSetting->name)</a>:
1925 @ </td><td>
1926 entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/,
1927 pSetting->def, 0);
1928 @ </td></tr>
1929 }
1930
--- src/fossil.copybutton.js
+++ src/fossil.copybutton.js
@@ -42,13 +42,11 @@
4242
4343
.oncopy: an optional callback function which is added as an event
4444
listener for the 'text-copied' event (see below). There is
4545
functionally no difference from setting this option or adding a
4646
'text-copied' event listener to the element, and this option is
47
- considered to be a convenience form of that. For the sake of
48
- framework-level consistency, the default value is a callback
49
- which passes the copy button to fossil.dom.flashOnce().
47
+ considered to be a convenience form of that.
5048
5149
Note that this function's own defaultOptions object holds default
5250
values for some options. Any changes made to that object affect
5351
any future calls to this function.
5452
@@ -62,25 +60,21 @@
6260
member is an object with a "text" property holding the copied
6361
text. Other properties may be added in the future. The event is
6462
not fired if copying to the clipboard fails (e.g. is not
6563
available in the current environment).
6664
67
- As a special case, the copy button's click handler is suppressed
68
- (becomes a no-op) for as long as the element has the CSS class
69
- "disabled". This allows elements which cannot be disabled via
70
- HTML attributes, e.g. a SPAN, to act as a copy button while still
71
- providing a way to disable them.
65
+ The copy button's click handler is suppressed (becomes a no-op)
66
+ for as long as the element has the "disabled" attribute.
7267
7368
Returns the copy-initialized element.
7469
7570
Example:
7671
7772
const button = fossil.copyButton('#my-copy-button', {
7873
copyFromId: 'some-other-element-id'
7974
});
8075
button.addEventListener('text-copied',function(ev){
81
- fossil.dom.flashOnce(ev.target);
8276
console.debug("Copied text:",ev.detail.text);
8377
});
8478
*/
8579
F.copyButton = function f(e, opt){
8680
if('string'===typeof e){
@@ -103,11 +97,11 @@
10397
e.addEventListener(
10498
'click',
10599
function(ev){
106100
ev.preventDefault();
107101
ev.stopPropagation();
108
- if(e.classList.contains('disabled')) return;
102
+ if(e.disabled) return; /* This check is probably redundant. */
109103
const txt = extract.call(opt);
110104
if(txt && D.copyTextToClipboard(txt)){
111105
e.dispatchEvent(new CustomEvent('text-copied',{
112106
detail: {text: txt}
113107
}));
@@ -116,15 +110,19 @@
116110
false
117111
);
118112
if('function' === typeof opt.oncopy){
119113
e.addEventListener('text-copied', opt.oncopy, false);
120114
}
115
+ /* Make sure the <button> contains a single nested <span>. */
116
+ if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){
117
+ D.append(D.clearElement(e), D.span());
118
+ }
121119
return e;
122120
};
123121
124122
F.copyButton.defaultOptions = {
125123
cssClass: 'copy-button',
126
- oncopy: D.flashOnce.eventHandler,
124
+ oncopy: undefined,
127125
style: {/*properties copied as-is into element.style*/}
128126
};
129127
130128
})(window.fossil);
131129
--- src/fossil.copybutton.js
+++ src/fossil.copybutton.js
@@ -42,13 +42,11 @@
42
43 .oncopy: an optional callback function which is added as an event
44 listener for the 'text-copied' event (see below). There is
45 functionally no difference from setting this option or adding a
46 'text-copied' event listener to the element, and this option is
47 considered to be a convenience form of that. For the sake of
48 framework-level consistency, the default value is a callback
49 which passes the copy button to fossil.dom.flashOnce().
50
51 Note that this function's own defaultOptions object holds default
52 values for some options. Any changes made to that object affect
53 any future calls to this function.
54
@@ -62,25 +60,21 @@
62 member is an object with a "text" property holding the copied
63 text. Other properties may be added in the future. The event is
64 not fired if copying to the clipboard fails (e.g. is not
65 available in the current environment).
66
67 As a special case, the copy button's click handler is suppressed
68 (becomes a no-op) for as long as the element has the CSS class
69 "disabled". This allows elements which cannot be disabled via
70 HTML attributes, e.g. a SPAN, to act as a copy button while still
71 providing a way to disable them.
72
73 Returns the copy-initialized element.
74
75 Example:
76
77 const button = fossil.copyButton('#my-copy-button', {
78 copyFromId: 'some-other-element-id'
79 });
80 button.addEventListener('text-copied',function(ev){
81 fossil.dom.flashOnce(ev.target);
82 console.debug("Copied text:",ev.detail.text);
83 });
84 */
85 F.copyButton = function f(e, opt){
86 if('string'===typeof e){
@@ -103,11 +97,11 @@
103 e.addEventListener(
104 'click',
105 function(ev){
106 ev.preventDefault();
107 ev.stopPropagation();
108 if(e.classList.contains('disabled')) return;
109 const txt = extract.call(opt);
110 if(txt && D.copyTextToClipboard(txt)){
111 e.dispatchEvent(new CustomEvent('text-copied',{
112 detail: {text: txt}
113 }));
@@ -116,15 +110,19 @@
116 false
117 );
118 if('function' === typeof opt.oncopy){
119 e.addEventListener('text-copied', opt.oncopy, false);
120 }
 
 
 
 
121 return e;
122 };
123
124 F.copyButton.defaultOptions = {
125 cssClass: 'copy-button',
126 oncopy: D.flashOnce.eventHandler,
127 style: {/*properties copied as-is into element.style*/}
128 };
129
130 })(window.fossil);
131
--- src/fossil.copybutton.js
+++ src/fossil.copybutton.js
@@ -42,13 +42,11 @@
42
43 .oncopy: an optional callback function which is added as an event
44 listener for the 'text-copied' event (see below). There is
45 functionally no difference from setting this option or adding a
46 'text-copied' event listener to the element, and this option is
47 considered to be a convenience form of that.
 
 
48
49 Note that this function's own defaultOptions object holds default
50 values for some options. Any changes made to that object affect
51 any future calls to this function.
52
@@ -62,25 +60,21 @@
60 member is an object with a "text" property holding the copied
61 text. Other properties may be added in the future. The event is
62 not fired if copying to the clipboard fails (e.g. is not
63 available in the current environment).
64
65 The copy button's click handler is suppressed (becomes a no-op)
66 for as long as the element has the "disabled" attribute.
 
 
 
67
68 Returns the copy-initialized element.
69
70 Example:
71
72 const button = fossil.copyButton('#my-copy-button', {
73 copyFromId: 'some-other-element-id'
74 });
75 button.addEventListener('text-copied',function(ev){
 
76 console.debug("Copied text:",ev.detail.text);
77 });
78 */
79 F.copyButton = function f(e, opt){
80 if('string'===typeof e){
@@ -103,11 +97,11 @@
97 e.addEventListener(
98 'click',
99 function(ev){
100 ev.preventDefault();
101 ev.stopPropagation();
102 if(e.disabled) return; /* This check is probably redundant. */
103 const txt = extract.call(opt);
104 if(txt && D.copyTextToClipboard(txt)){
105 e.dispatchEvent(new CustomEvent('text-copied',{
106 detail: {text: txt}
107 }));
@@ -116,15 +110,19 @@
110 false
111 );
112 if('function' === typeof opt.oncopy){
113 e.addEventListener('text-copied', opt.oncopy, false);
114 }
115 /* Make sure the <button> contains a single nested <span>. */
116 if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){
117 D.append(D.clearElement(e), D.span());
118 }
119 return e;
120 };
121
122 F.copyButton.defaultOptions = {
123 cssClass: 'copy-button',
124 oncopy: undefined,
125 style: {/*properties copied as-is into element.style*/}
126 };
127
128 })(window.fossil);
129
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -248,11 +248,11 @@
248248
const e = this.create(childType);
249249
if(parent) parent.appendChild(e);
250250
return e;
251251
};
252252
};
253
-
253
+
254254
dom.table = dom.createElemFactory('table');
255255
dom.thead = dom.createElemFactoryWithOptionalParent('thead');
256256
dom.tbody = dom.createElemFactoryWithOptionalParent('tbody');
257257
dom.tfoot = dom.createElemFactoryWithOptionalParent('tfoot');
258258
dom.tr = dom.createElemFactoryWithOptionalParent('tr');
@@ -381,12 +381,12 @@
381381
const domAddRemoveClass = function f(action,e){
382382
if(!f.rxSPlus){
383383
f.rxSPlus = /\s+/;
384384
f.applyAction = function(e,a,v){
385385
if(!e || !v
386
- /*silently skip empty strings/flasy
387
- values, for user convenience*/) return;
386
+ /*silently skip empty strings/falsy
387
+ values, for usage convenience*/) return;
388388
else if(e.forEach){
389389
e.forEach((E)=>E.classList[a](v));
390390
}else{
391391
e.classList[a](v);
392392
}
@@ -584,10 +584,11 @@
584584
}
585585
}
586586
return e;
587587
};
588588
589
+ /* Impl for dom.enable() and dom.disable(). */
589590
const enableDisable = function f(enable){
590591
var i = 1, n = arguments.length;
591592
for( ; i < n; ++i ){
592593
let e = arguments[i];
593594
if(e.forEach){
@@ -843,12 +844,12 @@
843844
/**
844845
Parses a string as HTML.
845846
846847
Usages:
847848
848
- Array (htmlString)
849
- DOMElement (DOMElement target, htmlString)
849
+ Array parseHtml(htmlString)
850
+ DOMElement parseHtml(DOMElement target, htmlString)
850851
851852
The first form parses the string as HTML and returns an Array of
852853
all elements parsed from it. If string is falsy then it returns
853854
an empty array.
854855
855856
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -248,11 +248,11 @@
248 const e = this.create(childType);
249 if(parent) parent.appendChild(e);
250 return e;
251 };
252 };
253
254 dom.table = dom.createElemFactory('table');
255 dom.thead = dom.createElemFactoryWithOptionalParent('thead');
256 dom.tbody = dom.createElemFactoryWithOptionalParent('tbody');
257 dom.tfoot = dom.createElemFactoryWithOptionalParent('tfoot');
258 dom.tr = dom.createElemFactoryWithOptionalParent('tr');
@@ -381,12 +381,12 @@
381 const domAddRemoveClass = function f(action,e){
382 if(!f.rxSPlus){
383 f.rxSPlus = /\s+/;
384 f.applyAction = function(e,a,v){
385 if(!e || !v
386 /*silently skip empty strings/flasy
387 values, for user convenience*/) return;
388 else if(e.forEach){
389 e.forEach((E)=>E.classList[a](v));
390 }else{
391 e.classList[a](v);
392 }
@@ -584,10 +584,11 @@
584 }
585 }
586 return e;
587 };
588
 
589 const enableDisable = function f(enable){
590 var i = 1, n = arguments.length;
591 for( ; i < n; ++i ){
592 let e = arguments[i];
593 if(e.forEach){
@@ -843,12 +844,12 @@
843 /**
844 Parses a string as HTML.
845
846 Usages:
847
848 Array (htmlString)
849 DOMElement (DOMElement target, htmlString)
850
851 The first form parses the string as HTML and returns an Array of
852 all elements parsed from it. If string is falsy then it returns
853 an empty array.
854
855
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -248,11 +248,11 @@
248 const e = this.create(childType);
249 if(parent) parent.appendChild(e);
250 return e;
251 };
252 };
253
254 dom.table = dom.createElemFactory('table');
255 dom.thead = dom.createElemFactoryWithOptionalParent('thead');
256 dom.tbody = dom.createElemFactoryWithOptionalParent('tbody');
257 dom.tfoot = dom.createElemFactoryWithOptionalParent('tfoot');
258 dom.tr = dom.createElemFactoryWithOptionalParent('tr');
@@ -381,12 +381,12 @@
381 const domAddRemoveClass = function f(action,e){
382 if(!f.rxSPlus){
383 f.rxSPlus = /\s+/;
384 f.applyAction = function(e,a,v){
385 if(!e || !v
386 /*silently skip empty strings/falsy
387 values, for usage convenience*/) return;
388 else if(e.forEach){
389 e.forEach((E)=>E.classList[a](v));
390 }else{
391 e.classList[a](v);
392 }
@@ -584,10 +584,11 @@
584 }
585 }
586 return e;
587 };
588
589 /* Impl for dom.enable() and dom.disable(). */
590 const enableDisable = function f(enable){
591 var i = 1, n = arguments.length;
592 for( ; i < n; ++i ){
593 let e = arguments[i];
594 if(e.forEach){
@@ -843,12 +844,12 @@
844 /**
845 Parses a string as HTML.
846
847 Usages:
848
849 Array parseHtml(htmlString)
850 DOMElement parseHtml(DOMElement target, htmlString)
851
852 The first form parses the string as HTML and returns an Array of
853 all elements parsed from it. If string is falsy then it returns
854 an empty array.
855
856
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -23,13 +23,10 @@
2323
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
2424
.replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
2525
.replace('?&','?');
2626
const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
2727
const lineTip = new F.PopupWidget({
28
- style: {
29
- cursor: 'pointer'
30
- },
3128
refresh: function(){
3229
const link = this.state.link;
3330
D.clearElement(link);
3431
if(lineState.start){
3532
const ls = [lineState.start];
@@ -48,23 +45,22 @@
4845
D.append(link, "No lines selected.");
4946
}
5047
},
5148
init: function(){
5249
const e = this.e;
53
- const btnCopy = D.span(),
54
- link = D.span();
50
+ const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button');
51
+ link = D.label('linenum-copy-button');
5552
this.state = {link};
5653
F.copyButton(btnCopy,{
5754
copyFromElement: link,
5855
extractText: ()=>link.dataset.url,
5956
oncopy: (ev)=>{
60
- D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
57
+ setTimeout(()=>lineTip.hide(), 400);
6158
// arguably too snazzy: F.toast.message("Copied link to clipboard.");
6259
}
6360
});
64
- this.e.addEventListener('click', ()=>btnCopy.click(), false);
65
- D.append(this.e, btnCopy, link)
61
+ D.append(this.e, btnCopy, link);
6662
}
6763
});
6864
6965
tbl.addEventListener('click', ()=>lineTip.hide(), true);
7066
7167
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -23,13 +23,10 @@
23 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
24 .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
25 .replace('?&','?');
26 const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
27 const lineTip = new F.PopupWidget({
28 style: {
29 cursor: 'pointer'
30 },
31 refresh: function(){
32 const link = this.state.link;
33 D.clearElement(link);
34 if(lineState.start){
35 const ls = [lineState.start];
@@ -48,23 +45,22 @@
48 D.append(link, "No lines selected.");
49 }
50 },
51 init: function(){
52 const e = this.e;
53 const btnCopy = D.span(),
54 link = D.span();
55 this.state = {link};
56 F.copyButton(btnCopy,{
57 copyFromElement: link,
58 extractText: ()=>link.dataset.url,
59 oncopy: (ev)=>{
60 D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
61 // arguably too snazzy: F.toast.message("Copied link to clipboard.");
62 }
63 });
64 this.e.addEventListener('click', ()=>btnCopy.click(), false);
65 D.append(this.e, btnCopy, link)
66 }
67 });
68
69 tbl.addEventListener('click', ()=>lineTip.hide(), true);
70
71
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -23,13 +23,10 @@
23 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
24 .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
25 .replace('?&','?');
26 const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
27 const lineTip = new F.PopupWidget({
 
 
 
28 refresh: function(){
29 const link = this.state.link;
30 D.clearElement(link);
31 if(lineState.start){
32 const ls = [lineState.start];
@@ -48,23 +45,22 @@
45 D.append(link, "No lines selected.");
46 }
47 },
48 init: function(){
49 const e = this.e;
50 const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button');
51 link = D.label('linenum-copy-button');
52 this.state = {link};
53 F.copyButton(btnCopy,{
54 copyFromElement: link,
55 extractText: ()=>link.dataset.url,
56 oncopy: (ev)=>{
57 setTimeout(()=>lineTip.hide(), 400);
58 // arguably too snazzy: F.toast.message("Copied link to clipboard.");
59 }
60 });
61 D.append(this.e, btnCopy, link);
 
62 }
63 });
64
65 tbl.addEventListener('click', ()=>lineTip.hide(), true);
66
67
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -901,14 +901,13 @@
901901
const cpId = 'copy-to-clipboard-'+id;
902902
/* ^^^ copy button element ID, needed for LABEL element
903903
pairing. Recall that we destroy all child elements of
904904
`content` each time we hit this block, so we can reuse
905905
that element ID on subsequent toggles. */
906
- const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId);
906
+ const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId);
907907
F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
908908
const lblCp = D.label(cpId, "Copy unformatted text");
909
- lblCp.addEventListener('click',()=>btnCp.click(), false);
910909
D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
911910
}
912911
delete e.$isToggling;
913912
D.append(content, child);
914913
return;
@@ -1161,11 +1160,11 @@
11611160
if(body && !body.style.fontSize){
11621161
/** _Attempt_ to force the iframe to inherit the message's text size
11631162
if the body has no explicit size set. On desktop systems
11641163
the size is apparently being inherited in that case, but on mobile
11651164
not. */
1166
- body.style.fontSize = window.getComputedStyle(msgObj.e.content);
1165
+ body.style.fontSize = window.getComputedStyle(msgObj.e.content).fontSize;
11671166
}
11681167
if('' === iframe.style.maxHeight){
11691168
/* Resize iframe height to fit the content. Workaround: if we
11701169
adjust the iframe height while it's hidden then its height
11711170
is 0, so we must briefly unhide it. */
@@ -1736,14 +1735,10 @@
17361735
(k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
17371736
);
17381737
return bxs;
17391738
})()/*drag/drop/paste*/;
17401739
1741
- const tzOffsetToString = function(off){
1742
- const hours = Math.round(off/60), min = Math.round(off % 30);
1743
- return ''+(hours + (min ? '.5' : ''));
1744
- };
17451740
const localTime8601 = function(d){
17461741
return [
17471742
d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
17481743
'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
17491744
].join('');
17501745
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -901,14 +901,13 @@
901 const cpId = 'copy-to-clipboard-'+id;
902 /* ^^^ copy button element ID, needed for LABEL element
903 pairing. Recall that we destroy all child elements of
904 `content` each time we hit this block, so we can reuse
905 that element ID on subsequent toggles. */
906 const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId);
907 F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
908 const lblCp = D.label(cpId, "Copy unformatted text");
909 lblCp.addEventListener('click',()=>btnCp.click(), false);
910 D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
911 }
912 delete e.$isToggling;
913 D.append(content, child);
914 return;
@@ -1161,11 +1160,11 @@
1161 if(body && !body.style.fontSize){
1162 /** _Attempt_ to force the iframe to inherit the message's text size
1163 if the body has no explicit size set. On desktop systems
1164 the size is apparently being inherited in that case, but on mobile
1165 not. */
1166 body.style.fontSize = window.getComputedStyle(msgObj.e.content);
1167 }
1168 if('' === iframe.style.maxHeight){
1169 /* Resize iframe height to fit the content. Workaround: if we
1170 adjust the iframe height while it's hidden then its height
1171 is 0, so we must briefly unhide it. */
@@ -1736,14 +1735,10 @@
1736 (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
1737 );
1738 return bxs;
1739 })()/*drag/drop/paste*/;
1740
1741 const tzOffsetToString = function(off){
1742 const hours = Math.round(off/60), min = Math.round(off % 30);
1743 return ''+(hours + (min ? '.5' : ''));
1744 };
1745 const localTime8601 = function(d){
1746 return [
1747 d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
1748 'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
1749 ].join('');
1750
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -901,14 +901,13 @@
901 const cpId = 'copy-to-clipboard-'+id;
902 /* ^^^ copy button element ID, needed for LABEL element
903 pairing. Recall that we destroy all child elements of
904 `content` each time we hit this block, so we can reuse
905 that element ID on subsequent toggles. */
906 const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId);
907 F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
908 const lblCp = D.label(cpId, "Copy unformatted text");
 
909 D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
910 }
911 delete e.$isToggling;
912 D.append(content, child);
913 return;
@@ -1161,11 +1160,11 @@
1160 if(body && !body.style.fontSize){
1161 /** _Attempt_ to force the iframe to inherit the message's text size
1162 if the body has no explicit size set. On desktop systems
1163 the size is apparently being inherited in that case, but on mobile
1164 not. */
1165 body.style.fontSize = window.getComputedStyle(msgObj.e.content).fontSize;
1166 }
1167 if('' === iframe.style.maxHeight){
1168 /* Resize iframe height to fit the content. Workaround: if we
1169 adjust the iframe height while it's hidden then its height
1170 is 0, so we must briefly unhide it. */
@@ -1736,14 +1735,10 @@
1735 (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
1736 );
1737 return bxs;
1738 })()/*drag/drop/paste*/;
1739
 
 
 
 
1740 const localTime8601 = function(d){
1741 return [
1742 d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
1743 'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
1744 ].join('');
1745
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -47,11 +47,11 @@
4747
document.body.classList.add('pikchrshow');
4848
P.e = { /* various DOM elements we work with... */
4949
previewTarget: E('#pikchrshow-output'),
5050
previewLegend: E('#pikchrshow-output-wrapper > legend'),
5151
previewCopyButton: D.attr(
52
- D.addClass(D.span(),'copy-button'),
52
+ D.addClass(D.button(),'copy-button'),
5353
'id','preview-copy-button'
5454
),
5555
previewModeLabel: D.label('preview-copy-button'),
5656
btnSubmit: E('#pikchr-submit-preview'),
5757
btnStash: E('#pikchr-stash'),
@@ -119,11 +119,10 @@
119119
}, false);
120120
121121
////////////////////////////////////////////////////////////
122122
// Setup clipboard-copy of markup/SVG...
123123
F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
124
- P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false);
125124
126125
////////////////////////////////////////////////////////////
127126
// Set up dark mode simulator...
128127
P.e.cbDarkMode.addEventListener('change', function(ev){
129128
if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
@@ -348,11 +347,11 @@
348347
D.addClass(preTgt, 'error');
349348
this.e.previewModeLabel.innerText = "Error";
350349
return;
351350
}
352351
D.removeClass(preTgt, 'error');
353
- D.removeClass(this.e.previewCopyButton, 'disabled');
352
+ this.e.previewCopyButton.disabled = false;
354353
D.removeClass(this.e.markupAlignWrapper, 'hidden');
355354
D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
356355
let label, svg;
357356
switch(this.previewMode){
358357
case 0:
@@ -427,11 +426,11 @@
427426
P.renderPreview();
428427
};
429428
}
430429
D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
431430
D.addClass(this.e.markupAlignWrapper, 'hidden');
432
- D.addClass(this.e.previewCopyButton, 'disabled');
431
+ this.e.previewCopyButton.disabled = true;
433432
const content = this.e.taContent.value.trim();
434433
this.response.raw = this.response.rawSvg = undefined;
435434
this.response.inputText = content;
436435
const sampleScript = fp.$_sampleScript;
437436
delete fp.$_sampleScript;
438437
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -47,11 +47,11 @@
47 document.body.classList.add('pikchrshow');
48 P.e = { /* various DOM elements we work with... */
49 previewTarget: E('#pikchrshow-output'),
50 previewLegend: E('#pikchrshow-output-wrapper > legend'),
51 previewCopyButton: D.attr(
52 D.addClass(D.span(),'copy-button'),
53 'id','preview-copy-button'
54 ),
55 previewModeLabel: D.label('preview-copy-button'),
56 btnSubmit: E('#pikchr-submit-preview'),
57 btnStash: E('#pikchr-stash'),
@@ -119,11 +119,10 @@
119 }, false);
120
121 ////////////////////////////////////////////////////////////
122 // Setup clipboard-copy of markup/SVG...
123 F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
124 P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false);
125
126 ////////////////////////////////////////////////////////////
127 // Set up dark mode simulator...
128 P.e.cbDarkMode.addEventListener('change', function(ev){
129 if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
@@ -348,11 +347,11 @@
348 D.addClass(preTgt, 'error');
349 this.e.previewModeLabel.innerText = "Error";
350 return;
351 }
352 D.removeClass(preTgt, 'error');
353 D.removeClass(this.e.previewCopyButton, 'disabled');
354 D.removeClass(this.e.markupAlignWrapper, 'hidden');
355 D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
356 let label, svg;
357 switch(this.previewMode){
358 case 0:
@@ -427,11 +426,11 @@
427 P.renderPreview();
428 };
429 }
430 D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
431 D.addClass(this.e.markupAlignWrapper, 'hidden');
432 D.addClass(this.e.previewCopyButton, 'disabled');
433 const content = this.e.taContent.value.trim();
434 this.response.raw = this.response.rawSvg = undefined;
435 this.response.inputText = content;
436 const sampleScript = fp.$_sampleScript;
437 delete fp.$_sampleScript;
438
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -47,11 +47,11 @@
47 document.body.classList.add('pikchrshow');
48 P.e = { /* various DOM elements we work with... */
49 previewTarget: E('#pikchrshow-output'),
50 previewLegend: E('#pikchrshow-output-wrapper > legend'),
51 previewCopyButton: D.attr(
52 D.addClass(D.button(),'copy-button'),
53 'id','preview-copy-button'
54 ),
55 previewModeLabel: D.label('preview-copy-button'),
56 btnSubmit: E('#pikchr-submit-preview'),
57 btnStash: E('#pikchr-stash'),
@@ -119,11 +119,10 @@
119 }, false);
120
121 ////////////////////////////////////////////////////////////
122 // Setup clipboard-copy of markup/SVG...
123 F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
 
124
125 ////////////////////////////////////////////////////////////
126 // Set up dark mode simulator...
127 P.e.cbDarkMode.addEventListener('change', function(ev){
128 if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
@@ -348,11 +347,11 @@
347 D.addClass(preTgt, 'error');
348 this.e.previewModeLabel.innerText = "Error";
349 return;
350 }
351 D.removeClass(preTgt, 'error');
352 this.e.previewCopyButton.disabled = false;
353 D.removeClass(this.e.markupAlignWrapper, 'hidden');
354 D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
355 let label, svg;
356 switch(this.previewMode){
357 case 0:
@@ -427,11 +426,11 @@
426 P.renderPreview();
427 };
428 }
429 D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
430 D.addClass(this.e.markupAlignWrapper, 'hidden');
431 this.e.previewCopyButton.disabled = true;
432 const content = this.e.taContent.value.trim();
433 this.response.raw = this.response.rawSvg = undefined;
434 this.response.inputText = content;
435 const sampleScript = fp.$_sampleScript;
436 delete fp.$_sampleScript;
437
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -312,11 +312,10 @@
312312
if(this.e.pikOut.dataset.pikchr){
313313
this.render(this.e.pikOut.dataset.pikchr);
314314
}
315315
}.bind(PS));
316316
F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
317
- PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false);
318317
319318
PS.addMsgHandler('working',function f(ev){
320319
switch(ev.data){
321320
case 'start': /* See notes in preStartWork(). */; return;
322321
case 'end':
323322
324323
ADDED src/fossil.page.ticket.js
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -312,11 +312,10 @@
312 if(this.e.pikOut.dataset.pikchr){
313 this.render(this.e.pikOut.dataset.pikchr);
314 }
315 }.bind(PS));
316 F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
317 PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false);
318
319 PS.addMsgHandler('working',function f(ev){
320 switch(ev.data){
321 case 'start': /* See notes in preStartWork(). */; return;
322 case 'end':
323
324 DDED src/fossil.page.ticket.js
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -312,11 +312,10 @@
312 if(this.e.pikOut.dataset.pikchr){
313 this.render(this.e.pikOut.dataset.pikchr);
314 }
315 }.bind(PS));
316 F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
 
317
318 PS.addMsgHandler('working',function f(ev){
319 switch(ev.data){
320 case 'start': /* See notes in preStartWork(). */; return;
321 case 'end':
322
323 DDED src/fossil.page.ticket.js
--- a/src/fossil.page.ticket.js
+++ b/src/fossil.page.ticket.js
@@ -0,0 +1,32 @@
1
+/*
2
+ * This script adds a checkbox to reverse the sorting on any body.tkt
3
+ * pages which contain a .tktCommentArea element.
4
+ */
5
+window.addEventListener( 'load', function() {
6
+ const tgt = document.querySelectorAll('.tktCommentArea');
7
+ if( !tgt ) return;
8
+ const F = globalThis.fossil, D = F.dom;
9
+ let i = 0;
10
+ for(const e of tgt) {
11
+ ++i;
12
+ const childs = e.querySelectorAll('.tktCommentEntry');
13
+ if( !childs || 1===childs.length ) continue;
14
+ const cbReverseKey = 'tktCommentArea:reverse';
15
+ const cbReverse = D.checkbox();
16
+ const cbId = cbReverseKey+':'+i;
17
+ cbReverse.setAttribute('id',cbId);
18
+ const widget = D.append(
19
+ D.div(),
20
+ cbReverse,
21
+ D.label(cbReverse, " Show newest first? ")
22
+ );
23
+ widget.classList.add('newest-first-controls');
24
+ e.parentElement.insertBefore(widget,e);
25
+ const cbReverseIt = ()=>{
26
+ e.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
27
+ F.storage.set(cbReverseKey, cbReverse.checked ? 1 : 0);
28
+ };
29
+ cbReverse.addEventListener('change', cbReverseIt, true);
30
+ cbReverse.checked = !!(+F.storage.get(cbReverseKey, 0));
31
+ };
32
+}); // window.addEventListener( 'load' ...
--- a/src/fossil.page.ticket.js
+++ b/src/fossil.page.ticket.js
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/fossil.page.ticket.js
+++ b/src/fossil.page.ticket.js
@@ -0,0 +1,32 @@
1 /*
2 * This script adds a checkbox to reverse the sorting on any body.tkt
3 * pages which contain a .tktCommentArea element.
4 */
5 window.addEventListener( 'load', function() {
6 const tgt = document.querySelectorAll('.tktCommentArea');
7 if( !tgt ) return;
8 const F = globalThis.fossil, D = F.dom;
9 let i = 0;
10 for(const e of tgt) {
11 ++i;
12 const childs = e.querySelectorAll('.tktCommentEntry');
13 if( !childs || 1===childs.length ) continue;
14 const cbReverseKey = 'tktCommentArea:reverse';
15 const cbReverse = D.checkbox();
16 const cbId = cbReverseKey+':'+i;
17 cbReverse.setAttribute('id',cbId);
18 const widget = D.append(
19 D.div(),
20 cbReverse,
21 D.label(cbReverse, " Show newest first? ")
22 );
23 widget.classList.add('newest-first-controls');
24 e.parentElement.insertBefore(widget,e);
25 const cbReverseIt = ()=>{
26 e.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
27 F.storage.set(cbReverseKey, cbReverse.checked ? 1 : 0);
28 };
29 cbReverse.addEventListener('change', cbReverseIt, true);
30 cbReverse.checked = !!(+F.storage.get(cbReverseKey, 0));
31 };
32 }); // window.addEventListener( 'load' ...
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -1234,13 +1234,13 @@
12341234
encodeURIComponent(a.filename)
12351235
].join(''),
12361236
"raw/"+a.src
12371237
].forEach(function(url){
12381238
const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
1239
- const urlCopy = D.span();
1239
+ const urlCopy = D.button();
12401240
const li = D.li(ul);
1241
- D.append(li, urlCopy, " ", imgUrl);
1241
+ D.append(li, urlCopy, imgUrl);
12421242
F.copyButton(urlCopy, {copyFromElement: imgUrl});
12431243
});
12441244
});
12451245
return this;
12461246
};
12471247
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -1234,13 +1234,13 @@
1234 encodeURIComponent(a.filename)
1235 ].join(''),
1236 "raw/"+a.src
1237 ].forEach(function(url){
1238 const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
1239 const urlCopy = D.span();
1240 const li = D.li(ul);
1241 D.append(li, urlCopy, " ", imgUrl);
1242 F.copyButton(urlCopy, {copyFromElement: imgUrl});
1243 });
1244 });
1245 return this;
1246 };
1247
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -1234,13 +1234,13 @@
1234 encodeURIComponent(a.filename)
1235 ].join(''),
1236 "raw/"+a.src
1237 ].forEach(function(url){
1238 const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
1239 const urlCopy = D.button();
1240 const li = D.li(ul);
1241 D.append(li, urlCopy, imgUrl);
1242 F.copyButton(urlCopy, {copyFromElement: imgUrl});
1243 });
1244 });
1245 return this;
1246 };
1247
+4 -4
--- src/graph.js
+++ src/graph.js
@@ -726,15 +726,15 @@
726726
function toggleDetail(){
727727
var id = parseInt(this.getAttribute('data-id'))
728728
var x = document.getElementById("detail-"+id);
729729
if( x.style.display=="inline" ){
730730
x.style.display="none";
731
- changeDisplayById("ellipsis-"+id,"inline");
731
+ document.getElementById("ellipsis-"+id).textContent = "...";
732732
changeDisplayById("links-"+id,"none");
733733
}else{
734734
x.style.display="inline";
735
- changeDisplayById("ellipsis-"+id,"none");
735
+ document.getElementById("ellipsis-"+id).textContent = "←";
736736
changeDisplayById("links-"+id,"inline");
737737
}
738738
checkHeight();
739739
}
740740
function scrollToSelected(){
@@ -764,12 +764,12 @@
764764
}
765765
if( tx.scrollToSelect ){
766766
scrollToSelected();
767767
}
768768
769
- /* Set the onclick= attributes for elements of the "Compact" display
770
- ** mode so that clicking turns the details on and off.
769
+ /* Set the onclick= attributes for elements of the "Compact" and
770
+ ** "Simple" views so that clicking turns the details on and off.
771771
*/
772772
var lx = topObj.getElementsByClassName('timelineEllipsis');
773773
var i;
774774
for(i=0; i<lx.length; i++){
775775
if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
776776
--- src/graph.js
+++ src/graph.js
@@ -726,15 +726,15 @@
726 function toggleDetail(){
727 var id = parseInt(this.getAttribute('data-id'))
728 var x = document.getElementById("detail-"+id);
729 if( x.style.display=="inline" ){
730 x.style.display="none";
731 changeDisplayById("ellipsis-"+id,"inline");
732 changeDisplayById("links-"+id,"none");
733 }else{
734 x.style.display="inline";
735 changeDisplayById("ellipsis-"+id,"none");
736 changeDisplayById("links-"+id,"inline");
737 }
738 checkHeight();
739 }
740 function scrollToSelected(){
@@ -764,12 +764,12 @@
764 }
765 if( tx.scrollToSelect ){
766 scrollToSelected();
767 }
768
769 /* Set the onclick= attributes for elements of the "Compact" display
770 ** mode so that clicking turns the details on and off.
771 */
772 var lx = topObj.getElementsByClassName('timelineEllipsis');
773 var i;
774 for(i=0; i<lx.length; i++){
775 if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
776
--- src/graph.js
+++ src/graph.js
@@ -726,15 +726,15 @@
726 function toggleDetail(){
727 var id = parseInt(this.getAttribute('data-id'))
728 var x = document.getElementById("detail-"+id);
729 if( x.style.display=="inline" ){
730 x.style.display="none";
731 document.getElementById("ellipsis-"+id).textContent = "...";
732 changeDisplayById("links-"+id,"none");
733 }else{
734 x.style.display="inline";
735 document.getElementById("ellipsis-"+id).textContent = "←";
736 changeDisplayById("links-"+id,"inline");
737 }
738 checkHeight();
739 }
740 function scrollToSelected(){
@@ -764,12 +764,12 @@
764 }
765 if( tx.scrollToSelect ){
766 scrollToSelected();
767 }
768
769 /* Set the onclick= attributes for elements of the "Compact" and
770 ** "Simple" views so that clicking turns the details on and off.
771 */
772 var lx = topObj.getElementsByClassName('timelineEllipsis');
773 var i;
774 for(i=0; i<lx.length; i++){
775 if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
776
+16 -12
--- src/href.js
+++ src/href.js
@@ -1,12 +1,12 @@
11
/* As an anti-robot defense, <a> elements are initially coded with the
22
** href= set to the honeypot, and <form> elements are initialized with
33
** action= set to the login page. The real values for href= and action=
44
** are held in data-href= and data-action=. The following code moves
55
** data-href= into href= and data-action= into action= for all
6
-** <a> and <form> elements, after delay and maybe also after mouse
7
-** movement is seen.
6
+** <a> and <form> elements, after CSS has been loaded, and after a delay,
7
+** and maybe also after mouse movement is seen.
88
**
99
** Before sourcing this script, create a separate <script> element
1010
** (with type='application/json' to avoid Content Security Policy issues)
1111
** containing:
1212
**
@@ -18,20 +18,23 @@
1818
** until the first mousedown event that occurs after the timer expires.
1919
*/
2020
var antiRobot = 0;
2121
function antiRobotGo(){
2222
if( antiRobot!=3 ) return;
23
- antiRobot = 7;
24
- var anchors = document.getElementsByTagName("a");
25
- for(var i=0; i<anchors.length; i++){
26
- var j = anchors[i];
27
- if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
28
- }
29
- var forms = document.getElementsByTagName("form");
30
- for(var i=0; i<forms.length; i++){
31
- var j = forms[i];
32
- if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
23
+ var z = window.getComputedStyle(document.body).zIndex;
24
+ if( z==='0' || z===0 ){
25
+ antiRobot = 7;
26
+ var anchors = document.getElementsByTagName("a");
27
+ for(var i=0; i<anchors.length; i++){
28
+ var j = anchors[i];
29
+ if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
30
+ }
31
+ var forms = document.getElementsByTagName("form");
32
+ for(var i=0; i<forms.length; i++){
33
+ var j = forms[i];
34
+ if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
35
+ }
3336
}
3437
}
3538
function antiRobotDefense(){
3639
var x = document.getElementById("href-data");
3740
var jx = x.textContent || x.innerText;
@@ -56,8 +59,9 @@
5659
antiRobotGo();
5760
}, g.delay)
5861
}else{
5962
antiRobot |= 1;
6063
}
64
+ window.addEventListener('load',antiRobotGo);
6165
antiRobotGo();
6266
}
6367
antiRobotDefense();
6468
--- src/href.js
+++ src/href.js
@@ -1,12 +1,12 @@
1 /* As an anti-robot defense, <a> elements are initially coded with the
2 ** href= set to the honeypot, and <form> elements are initialized with
3 ** action= set to the login page. The real values for href= and action=
4 ** are held in data-href= and data-action=. The following code moves
5 ** data-href= into href= and data-action= into action= for all
6 ** <a> and <form> elements, after delay and maybe also after mouse
7 ** movement is seen.
8 **
9 ** Before sourcing this script, create a separate <script> element
10 ** (with type='application/json' to avoid Content Security Policy issues)
11 ** containing:
12 **
@@ -18,20 +18,23 @@
18 ** until the first mousedown event that occurs after the timer expires.
19 */
20 var antiRobot = 0;
21 function antiRobotGo(){
22 if( antiRobot!=3 ) return;
23 antiRobot = 7;
24 var anchors = document.getElementsByTagName("a");
25 for(var i=0; i<anchors.length; i++){
26 var j = anchors[i];
27 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
28 }
29 var forms = document.getElementsByTagName("form");
30 for(var i=0; i<forms.length; i++){
31 var j = forms[i];
32 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
 
 
 
33 }
34 }
35 function antiRobotDefense(){
36 var x = document.getElementById("href-data");
37 var jx = x.textContent || x.innerText;
@@ -56,8 +59,9 @@
56 antiRobotGo();
57 }, g.delay)
58 }else{
59 antiRobot |= 1;
60 }
 
61 antiRobotGo();
62 }
63 antiRobotDefense();
64
--- src/href.js
+++ src/href.js
@@ -1,12 +1,12 @@
1 /* As an anti-robot defense, <a> elements are initially coded with the
2 ** href= set to the honeypot, and <form> elements are initialized with
3 ** action= set to the login page. The real values for href= and action=
4 ** are held in data-href= and data-action=. The following code moves
5 ** data-href= into href= and data-action= into action= for all
6 ** <a> and <form> elements, after CSS has been loaded, and after a delay,
7 ** and maybe also after mouse movement is seen.
8 **
9 ** Before sourcing this script, create a separate <script> element
10 ** (with type='application/json' to avoid Content Security Policy issues)
11 ** containing:
12 **
@@ -18,20 +18,23 @@
18 ** until the first mousedown event that occurs after the timer expires.
19 */
20 var antiRobot = 0;
21 function antiRobotGo(){
22 if( antiRobot!=3 ) return;
23 var z = window.getComputedStyle(document.body).zIndex;
24 if( z==='0' || z===0 ){
25 antiRobot = 7;
26 var anchors = document.getElementsByTagName("a");
27 for(var i=0; i<anchors.length; i++){
28 var j = anchors[i];
29 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
30 }
31 var forms = document.getElementsByTagName("form");
32 for(var i=0; i<forms.length; i++){
33 var j = forms[i];
34 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
35 }
36 }
37 }
38 function antiRobotDefense(){
39 var x = document.getElementById("href-data");
40 var jx = x.textContent || x.innerText;
@@ -56,8 +59,9 @@
59 antiRobotGo();
60 }, g.delay)
61 }else{
62 antiRobot |= 1;
63 }
64 window.addEventListener('load',antiRobotGo);
65 antiRobotGo();
66 }
67 antiRobotDefense();
68
+27 -10
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141141
Blob *pHdr, /* construct the header here */
142142
Blob *pLogin, /* Login card header value or NULL */
143143
const char *zAltMimetype /* Alternative mimetype */
144144
){
145145
int nPayload = pPayload ? blob_size(pPayload) : 0;
146
+ const char *zPath;
146147
147148
blob_zero(pHdr);
149
+ if( g.url.subpath ){
150
+ zPath = g.url.subpath;
151
+ }else if( g.url.path==0 || g.url.path[0]==0 ){
152
+ zPath = "/";
153
+ }else{
154
+ zPath = g.url.path;
155
+ }
148156
blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149
- nPayload>0 ? "POST" : "GET",
150
- (g.url.path && g.url.path[0]) ? g.url.path : "/");
157
+ nPayload>0 ? "POST" : "GET", zPath);
151158
if( g.url.proxyAuth ){
152159
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153160
}
154161
if( g.zHttpAuth && g.zHttpAuth[0] ){
155162
const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
459466
/* Activate the PATH= auxiliary argument to the ssh command if that
460467
** is called for.
461468
*/
462469
if( g.url.isSsh
463470
&& (g.url.flags & URL_SSH_RETRY)==0
471
+ && g.db!=0
464472
&& ssh_needs_path_argument(g.url.hostname, -1)
465473
){
466474
g.url.flags |= URL_SSH_PATH;
467475
}
468476
@@ -678,19 +686,21 @@
678686
&& (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
679687
&& (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
680688
){
681689
/* Retry after flipping the SSH_PATH setting */
682690
transport_close(&g.url);
683
- fossil_print(
684
- "First attempt to run fossil on %s using SSH failed.\n"
685
- "Retrying %s the PATH= argument.\n",
686
- g.url.hostname,
687
- (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
688
- );
691
+ if( (mHttpFlags & HTTP_QUIET)==0 ){
692
+ fossil_print(
693
+ "First attempt to run fossil on %s using SSH failed.\n"
694
+ "Retrying %s the PATH= argument.\n",
695
+ g.url.hostname,
696
+ (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
697
+ );
698
+ }
689699
g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
690700
rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
691
- if( rc==0 ){
701
+ if( rc==0 && g.db!=0 ){
692702
(void)ssh_needs_path_argument(g.url.hostname,
693703
(g.url.flags & URL_SSH_PATH)!=0);
694704
}
695705
return rc;
696706
}else{
@@ -808,17 +818,19 @@
808818
** Options:
809819
** --compress Use ZLIB compression on the payload
810820
** --mimetype TYPE Mimetype of the payload
811821
** --no-cert-verify Disable TLS cert verification
812822
** --out FILE Store the reply in FILE
823
+** --subpath PATH HTTP request path for ssh: and file: URLs
813824
** -v Verbose output
814825
** --xfer PAYLOAD in a Fossil xfer protocol message
815826
*/
816827
void test_httpmsg_command(void){
817828
const char *zMimetype;
818829
const char *zInFile;
819830
const char *zOutFile;
831
+ const char *zSubpath;
820832
Blob in, out;
821833
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
822834
823835
zMimetype = find_option("mimetype",0,1);
824836
zOutFile = find_option("out","o",1);
@@ -832,10 +844,11 @@
832844
if( find_option("xfer",0,0)!=0 ){
833845
mHttpFlags |= HTTP_USE_LOGIN;
834846
mHttpFlags &= ~HTTP_GENERIC;
835847
}
836848
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
849
+ zSubpath = find_option("subpath",0,1);
837850
verify_all_options();
838851
if( g.argc<3 || g.argc>5 ){
839852
usage("URL ?PAYLOAD? ?OUTPUT?");
840853
}
841854
zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +859,15 @@
846859
}
847860
zOutFile = g.argv[4];
848861
}
849862
url_parse(g.argv[2], 0);
850863
if( g.url.protocol[0]!='h' ){
851
- fossil_fatal("the %s command supports only http: and https:", g.argv[1]);
864
+ if( zSubpath==0 ){
865
+ fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
866
+ }else{
867
+ g.url.subpath = fossil_strdup(zSubpath);
868
+ }
852869
}
853870
if( zInFile ){
854871
blob_read_from_file(&in, zInFile, ExtFILE);
855872
if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
856873
if( fossil_strcmp(zInFile,"-")==0 ){
857874
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
 
146
147 blob_zero(pHdr);
 
 
 
 
 
 
 
148 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149 nPayload>0 ? "POST" : "GET",
150 (g.url.path && g.url.path[0]) ? g.url.path : "/");
151 if( g.url.proxyAuth ){
152 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153 }
154 if( g.zHttpAuth && g.zHttpAuth[0] ){
155 const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
459 /* Activate the PATH= auxiliary argument to the ssh command if that
460 ** is called for.
461 */
462 if( g.url.isSsh
463 && (g.url.flags & URL_SSH_RETRY)==0
 
464 && ssh_needs_path_argument(g.url.hostname, -1)
465 ){
466 g.url.flags |= URL_SSH_PATH;
467 }
468
@@ -678,19 +686,21 @@
678 && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
679 && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
680 ){
681 /* Retry after flipping the SSH_PATH setting */
682 transport_close(&g.url);
683 fossil_print(
684 "First attempt to run fossil on %s using SSH failed.\n"
685 "Retrying %s the PATH= argument.\n",
686 g.url.hostname,
687 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
688 );
 
 
689 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
690 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
691 if( rc==0 ){
692 (void)ssh_needs_path_argument(g.url.hostname,
693 (g.url.flags & URL_SSH_PATH)!=0);
694 }
695 return rc;
696 }else{
@@ -808,17 +818,19 @@
808 ** Options:
809 ** --compress Use ZLIB compression on the payload
810 ** --mimetype TYPE Mimetype of the payload
811 ** --no-cert-verify Disable TLS cert verification
812 ** --out FILE Store the reply in FILE
 
813 ** -v Verbose output
814 ** --xfer PAYLOAD in a Fossil xfer protocol message
815 */
816 void test_httpmsg_command(void){
817 const char *zMimetype;
818 const char *zInFile;
819 const char *zOutFile;
 
820 Blob in, out;
821 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
822
823 zMimetype = find_option("mimetype",0,1);
824 zOutFile = find_option("out","o",1);
@@ -832,10 +844,11 @@
832 if( find_option("xfer",0,0)!=0 ){
833 mHttpFlags |= HTTP_USE_LOGIN;
834 mHttpFlags &= ~HTTP_GENERIC;
835 }
836 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
 
837 verify_all_options();
838 if( g.argc<3 || g.argc>5 ){
839 usage("URL ?PAYLOAD? ?OUTPUT?");
840 }
841 zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +859,15 @@
846 }
847 zOutFile = g.argv[4];
848 }
849 url_parse(g.argv[2], 0);
850 if( g.url.protocol[0]!='h' ){
851 fossil_fatal("the %s command supports only http: and https:", g.argv[1]);
 
 
 
 
852 }
853 if( zInFile ){
854 blob_read_from_file(&in, zInFile, ExtFILE);
855 if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
856 if( fossil_strcmp(zInFile,"-")==0 ){
857
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
146 const char *zPath;
147
148 blob_zero(pHdr);
149 if( g.url.subpath ){
150 zPath = g.url.subpath;
151 }else if( g.url.path==0 || g.url.path[0]==0 ){
152 zPath = "/";
153 }else{
154 zPath = g.url.path;
155 }
156 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
157 nPayload>0 ? "POST" : "GET", zPath);
 
158 if( g.url.proxyAuth ){
159 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
160 }
161 if( g.zHttpAuth && g.zHttpAuth[0] ){
162 const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
466 /* Activate the PATH= auxiliary argument to the ssh command if that
467 ** is called for.
468 */
469 if( g.url.isSsh
470 && (g.url.flags & URL_SSH_RETRY)==0
471 && g.db!=0
472 && ssh_needs_path_argument(g.url.hostname, -1)
473 ){
474 g.url.flags |= URL_SSH_PATH;
475 }
476
@@ -678,19 +686,21 @@
686 && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
687 && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
688 ){
689 /* Retry after flipping the SSH_PATH setting */
690 transport_close(&g.url);
691 if( (mHttpFlags & HTTP_QUIET)==0 ){
692 fossil_print(
693 "First attempt to run fossil on %s using SSH failed.\n"
694 "Retrying %s the PATH= argument.\n",
695 g.url.hostname,
696 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
697 );
698 }
699 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
700 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
701 if( rc==0 && g.db!=0 ){
702 (void)ssh_needs_path_argument(g.url.hostname,
703 (g.url.flags & URL_SSH_PATH)!=0);
704 }
705 return rc;
706 }else{
@@ -808,17 +818,19 @@
818 ** Options:
819 ** --compress Use ZLIB compression on the payload
820 ** --mimetype TYPE Mimetype of the payload
821 ** --no-cert-verify Disable TLS cert verification
822 ** --out FILE Store the reply in FILE
823 ** --subpath PATH HTTP request path for ssh: and file: URLs
824 ** -v Verbose output
825 ** --xfer PAYLOAD in a Fossil xfer protocol message
826 */
827 void test_httpmsg_command(void){
828 const char *zMimetype;
829 const char *zInFile;
830 const char *zOutFile;
831 const char *zSubpath;
832 Blob in, out;
833 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
834
835 zMimetype = find_option("mimetype",0,1);
836 zOutFile = find_option("out","o",1);
@@ -832,10 +844,11 @@
844 if( find_option("xfer",0,0)!=0 ){
845 mHttpFlags |= HTTP_USE_LOGIN;
846 mHttpFlags &= ~HTTP_GENERIC;
847 }
848 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
849 zSubpath = find_option("subpath",0,1);
850 verify_all_options();
851 if( g.argc<3 || g.argc>5 ){
852 usage("URL ?PAYLOAD? ?OUTPUT?");
853 }
854 zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +859,15 @@
859 }
860 zOutFile = g.argv[4];
861 }
862 url_parse(g.argv[2], 0);
863 if( g.url.protocol[0]!='h' ){
864 if( zSubpath==0 ){
865 fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
866 }else{
867 g.url.subpath = fossil_strdup(zSubpath);
868 }
869 }
870 if( zInFile ){
871 blob_read_from_file(&in, zInFile, ExtFILE);
872 if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
873 if( fossil_strcmp(zInFile,"-")==0 ){
874
+73 -41
--- src/info.c
+++ src/info.c
@@ -401,34 +401,36 @@
401401
@ </span></div>
402402
if( pCfg ){
403403
append_diff(zOld, zNew, pCfg);
404404
}
405405
}else{
406
+ const char *zCkin2 =
407
+ mprintf(validate16(zCkin, -1) ? "%!S" : "%T"/*works-like:"%s"*/, zCkin);
406408
if( zOld && zNew ){
407409
if( fossil_strcmp(zOld, zNew)!=0 ){
408410
if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
409411
@ Renamed and modified
410
- @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\
412
+ @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\
411413
@ %h(zOldName)</a>
412414
@ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
413
- @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
415
+ @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
414416
@ %h(zName)</a>
415417
@ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
416418
}else{
417
- @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
419
+ @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
418420
@ %h(zName)</a>
419421
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
420422
@ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
421423
}
422424
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
423425
@ Name change
424
- @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\
426
+ @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\
425427
@ %h(zOldName)</a>
426
- @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
428
+ @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
427429
@ %h(zName)</a>.
428430
}else{
429
- @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
431
+ @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
430432
@ %h(zName)</a> became
431433
if( mperm==PERM_EXE ){
432434
@ executable with contents
433435
}else if( mperm==PERM_LNK ){
434436
@ a symlink with target
@@ -436,14 +438,14 @@
436438
@ a regular file with contents
437439
}
438440
@ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
439441
}
440442
}else if( zOld ){
441
- @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
443
+ @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zOld,zCkin2))\
442444
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
443445
}else{
444
- @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
446
+ @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
445447
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
446448
}
447449
if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
448450
if( pCfg ){
449451
@ </span></div>
@@ -643,10 +645,12 @@
643645
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
644646
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
645647
}else{
646648
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
647649
}
650
+ @ <div class="section" id="changes_section">Changes</div>
651
+ DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
648652
@ <div class="sectionmenu info-changes-menu">
649653
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
650654
if( diffType!=1 ){
651655
@ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
652656
}
@@ -718,10 +722,16 @@
718722
blob_reset(&old);
719723
blob_reset(&new);
720724
}
721725
}
722726
db_finalize(&q);
727
+ @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
728
+ @ document.getElementById('changes_section').textContent = 'Changes ' +
729
+ @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
730
+ @ '+%d(g.diffCnt[1]) ' +
731
+ @ '−%d(g.diffCnt[2]))'
732
+ @ </script>
723733
append_diff_javascript(diffType);
724734
}
725735
726736
/*
727737
** Render a web-page diff of the changes in the working check-out to
@@ -741,10 +751,12 @@
741751
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
742752
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
743753
}else{
744754
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
745755
}
756
+ @ <div class="section" id="changes_section">Changes</div>
757
+ DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
746758
@ <div class="sectionmenu info-changes-menu">
747759
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
748760
if( diffType!=1 ){
749761
@ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
750762
@ Unified&nbsp;Diff</a>
@@ -803,10 +815,16 @@
803815
}
804816
fossil_free(zLhs);
805817
fossil_free(zRhs);
806818
}
807819
db_finalize(&q);
820
+ @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
821
+ @ document.getElementById('changes_section').textContent = 'Changes ' +
822
+ @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
823
+ @ '+%d(g.diffCnt[1]) ' +
824
+ @ '−%d(g.diffCnt[2]))'
825
+ @ </script>
808826
append_diff_javascript(diffType);
809827
}
810828
811829
/*
812830
** WEBPAGE: ckout
@@ -918,11 +936,11 @@
918936
@ No such object: %h(zName)
919937
style_finish_page();
920938
return;
921939
}
922940
zRe = P("regex");
923
- if( zRe ) re_compile(&pRe, zRe, 0);
941
+ if( zRe ) fossil_re_compile(&pRe, zRe, 0);
924942
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
925943
zParent = db_text(0,
926944
"SELECT uuid FROM plink, blob"
927945
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
928946
rid
@@ -975,34 +993,23 @@
975993
@ <tr><th>Comment:</th><td class="infoComment">\
976994
@ %!W(zEComment?zEComment:zComment)</td></tr>
977995
978996
/* The Download: line */
979997
if( g.perm.Zip ){
980
- char *zPJ = db_get("short-project-name", 0);
981
- char *zUrl;
982
- Blob projName;
983
- int jj;
984
- if( zPJ==0 ) zPJ = db_get("project-name", "unnamed");
985
- blob_zero(&projName);
986
- blob_append(&projName, zPJ, -1);
987
- blob_trim(&projName);
988
- zPJ = blob_str(&projName);
989
- for(jj=0; zPJ[jj]; jj++){
990
- if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){
991
- zPJ[jj] = '_';
992
- }
993
- }
994
- zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
995998
@ <tr><th>Downloads:</th><td>
996
- @ %z(href("%s",zUrl))Tarball</a>
997
- @ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
998
- if( g.zLogin!=0 ){
999
- @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
1000
- @ SQL archive</a></td></tr>
1001
- }
1002
- fossil_free(zUrl);
1003
- blob_reset(&projName);
999
+ if( robot_would_be_restricted("download") ){
1000
+ @ See separate %z(href("%R/rchvdwnld/%!S",zUuid))download page</a>
1001
+ }else{
1002
+ char *zBase = archive_base_name(rid);
1003
+ @ %z(href("%R/tarball/%s.tar.gz",zBase))Tarball</a>
1004
+ @ | %z(href("%R/zip/%s.zip",zBase))ZIP archive</a>
1005
+ if( g.zLogin!=0 ){
1006
+ @ | %z(href("%R/sqlar/%s.sqlar",zBase))\
1007
+ @ SQL archive</a></td></tr>
1008
+ }
1009
+ fossil_free(zBase);
1010
+ }
10041011
}
10051012
10061013
@ <tr><th>Timelines:</th><td>
10071014
@ %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
10081015
if( zParent ){
@@ -1167,15 +1174,16 @@
11671174
}
11681175
render_backlink_graph(zUuid,
11691176
"<div class=\"section accordion\">References</div>\n");
11701177
@ <div class="section accordion">Context</div><div class="accordion_panel">
11711178
render_checkin_context(rid, 0, 0, 0);
1172
- @ </div><div class="section accordion">Changes</div>
1179
+ @ </div><div class="section accordion" id="changes_section">Changes</div>
11731180
@ <div class="accordion_panel">
11741181
@ <div class="sectionmenu info-changes-menu">
11751182
/* ^^^ .info-changes-menu is used by diff scroll sync */
11761183
pCfg = construct_diff_flags(diffType, &DCfg);
1184
+ DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
11771185
DCfg.pRe = pRe;
11781186
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
11791187
if( diffType!=1 ){
11801188
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
11811189
@ Unified&nbsp;Diff</a>
@@ -1227,10 +1235,18 @@
12271235
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
12281236
pCfg,mperm);
12291237
}
12301238
db_finalize(&q3);
12311239
@ </div>
1240
+ if( diffType!=0 ){
1241
+ @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
1242
+ @ document.getElementById('changes_section').textContent = 'Changes ' +
1243
+ @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
1244
+ @ '+%d(g.diffCnt[1]) ' +
1245
+ @ '−%d(g.diffCnt[2]))'
1246
+ @ </script>
1247
+ }
12321248
append_diff_javascript(diffType);
12331249
style_finish_page();
12341250
}
12351251
12361252
/*
@@ -1414,17 +1430,18 @@
14141430
Blob qpGlob; /* glob= query parameter for generated links */
14151431
int bInvert = PB("inv");
14161432
14171433
login_check_credentials();
14181434
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1435
+ if( robot_restrict("diff") ) return;
14191436
login_anonymous_available();
14201437
fossil_nice_default();
14211438
blob_init(&qp, 0, 0);
14221439
blob_init(&qpGlob, 0, 0);
14231440
diffType = preferred_diff_type();
14241441
zRe = P("regex");
1425
- if( zRe ) re_compile(&pRe, zRe, 0);
1442
+ if( zRe ) fossil_re_compile(&pRe, zRe, 0);
14261443
zBranch = P("branch");
14271444
if( zBranch && zBranch[0]==0 ) zBranch = 0;
14281445
if( zBranch ){
14291446
blob_appendf(&qp, "branch=%T", zBranch);
14301447
zMergeOrigin = mprintf("merge-in:%s", zBranch);
@@ -1918,18 +1935,31 @@
19181935
** * The "preferred-diff-type" setting
19191936
** * 1 for mobile and 2 for desktop, based on the UserAgent
19201937
*/
19211938
int preferred_diff_type(void){
19221939
int dflt;
1940
+ int res;
1941
+ int isBot;
19231942
static char zDflt[2]
19241943
/*static b/c cookie_link_parameter() does not copy it!*/;
1925
- dflt = db_get_int("preferred-diff-type",-99);
1926
- if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1944
+ if( robot_would_be_restricted("diff") ){
1945
+ dflt = 0;
1946
+ isBot = 1;
1947
+ }else{
1948
+ dflt = db_get_int("preferred-diff-type",-99);
1949
+ if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1950
+ isBot = 0;
1951
+ }
19271952
zDflt[0] = dflt + '0';
19281953
zDflt[1] = 0;
19291954
cookie_link_parameter("diff","diff", zDflt);
1930
- return atoi(PD_NoBot("diff",zDflt));
1955
+ res = atoi(PD_NoBot("diff",zDflt));
1956
+ if( isBot && res>0 && robot_restrict("diff") ){
1957
+ cgi_reply();
1958
+ fossil_exit(0);
1959
+ }
1960
+ return res;
19311961
}
19321962
19331963
19341964
/*
19351965
** WEBPAGE: fdiff
@@ -1967,10 +1997,11 @@
19671997
int verbose = PB("verbose");
19681998
DiffConfig DCfg;
19691999
19702000
login_check_credentials();
19712001
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2002
+ if( robot_restrict("diff") ) return;
19722003
diff_config_init(&DCfg, 0);
19732004
diffType = preferred_diff_type();
19742005
if( P("from") && P("to") ){
19752006
v1 = artifact_from_ci_and_filename("from");
19762007
v2 = artifact_from_ci_and_filename("to");
@@ -2009,11 +2040,11 @@
20092040
}
20102041
db_finalize(&q);
20112042
}
20122043
zRe = P("regex");
20132044
cgi_check_for_malice();
2014
- if( zRe ) re_compile(&pRe, zRe, 0);
2045
+ if( zRe ) fossil_re_compile(&pRe, zRe, 0);
20152046
if( verbose ) objdescFlags |= OBJDESC_DETAIL;
20162047
if( isPatch ){
20172048
Blob c1, c2, *pOut;
20182049
DiffConfig DCfg;
20192050
pOut = cgi_output_blob();
@@ -2407,15 +2438,15 @@
24072438
object_description(rid, objdescFlags, 0, &downloadName);
24082439
style_submenu_element("Download", "%R/raw/%s?at=%T",
24092440
zUuid, file_tail(blob_str(&downloadName)));
24102441
@ <hr>
24112442
content_get(rid, &content);
2412
- if( !g.isHuman ){
2443
+ if( blob_size(&content)>100000 ){
24132444
/* Prevent robots from running hexdump on megabyte-sized source files
24142445
** and there by eating up lots of CPU time and bandwidth. There is
24152446
** no good reason for a robot to need a hexdump. */
2416
- @ <p>A hex dump of this file is not available.
2447
+ @ <p>A hex dump of this file is not available because it is too large.
24172448
@ Please download the raw binary file and generate a hex dump yourself.</p>
24182449
}else{
24192450
@ <blockquote><pre>
24202451
hexdump(&content);
24212452
@ </pre></blockquote>
@@ -2915,11 +2946,12 @@
29152946
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
29162947
}
29172948
db_finalize(&q);
29182949
}
29192950
if( !docOnly ){
2920
- style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName));
2951
+ style_submenu_element("Download", "%R/raw/%s?at=%T",
2952
+ zUuid, file_tail(blob_str(&downloadName)));
29212953
if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
29222954
style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
29232955
}
29242956
}
29252957
if( zMime ){
29262958
--- src/info.c
+++ src/info.c
@@ -401,34 +401,36 @@
401 @ </span></div>
402 if( pCfg ){
403 append_diff(zOld, zNew, pCfg);
404 }
405 }else{
 
 
406 if( zOld && zNew ){
407 if( fossil_strcmp(zOld, zNew)!=0 ){
408 if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
409 @ Renamed and modified
410 @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\
411 @ %h(zOldName)</a>
412 @ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
413 @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
414 @ %h(zName)</a>
415 @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
416 }else{
417 @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
418 @ %h(zName)</a>
419 @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
420 @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
421 }
422 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
423 @ Name change
424 @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\
425 @ %h(zOldName)</a>
426 @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
427 @ %h(zName)</a>.
428 }else{
429 @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
430 @ %h(zName)</a> became
431 if( mperm==PERM_EXE ){
432 @ executable with contents
433 }else if( mperm==PERM_LNK ){
434 @ a symlink with target
@@ -436,14 +438,14 @@
436 @ a regular file with contents
437 }
438 @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
439 }
440 }else if( zOld ){
441 @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
442 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
443 }else{
444 @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
445 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
446 }
447 if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
448 if( pCfg ){
449 @ </span></div>
@@ -643,10 +645,12 @@
643 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
644 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
645 }else{
646 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
647 }
 
 
648 @ <div class="sectionmenu info-changes-menu">
649 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
650 if( diffType!=1 ){
651 @ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
652 }
@@ -718,10 +722,16 @@
718 blob_reset(&old);
719 blob_reset(&new);
720 }
721 }
722 db_finalize(&q);
 
 
 
 
 
 
723 append_diff_javascript(diffType);
724 }
725
726 /*
727 ** Render a web-page diff of the changes in the working check-out to
@@ -741,10 +751,12 @@
741 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
742 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
743 }else{
744 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
745 }
 
 
746 @ <div class="sectionmenu info-changes-menu">
747 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
748 if( diffType!=1 ){
749 @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
750 @ Unified&nbsp;Diff</a>
@@ -803,10 +815,16 @@
803 }
804 fossil_free(zLhs);
805 fossil_free(zRhs);
806 }
807 db_finalize(&q);
 
 
 
 
 
 
808 append_diff_javascript(diffType);
809 }
810
811 /*
812 ** WEBPAGE: ckout
@@ -918,11 +936,11 @@
918 @ No such object: %h(zName)
919 style_finish_page();
920 return;
921 }
922 zRe = P("regex");
923 if( zRe ) re_compile(&pRe, zRe, 0);
924 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
925 zParent = db_text(0,
926 "SELECT uuid FROM plink, blob"
927 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
928 rid
@@ -975,34 +993,23 @@
975 @ <tr><th>Comment:</th><td class="infoComment">\
976 @ %!W(zEComment?zEComment:zComment)</td></tr>
977
978 /* The Download: line */
979 if( g.perm.Zip ){
980 char *zPJ = db_get("short-project-name", 0);
981 char *zUrl;
982 Blob projName;
983 int jj;
984 if( zPJ==0 ) zPJ = db_get("project-name", "unnamed");
985 blob_zero(&projName);
986 blob_append(&projName, zPJ, -1);
987 blob_trim(&projName);
988 zPJ = blob_str(&projName);
989 for(jj=0; zPJ[jj]; jj++){
990 if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){
991 zPJ[jj] = '_';
992 }
993 }
994 zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
995 @ <tr><th>Downloads:</th><td>
996 @ %z(href("%s",zUrl))Tarball</a>
997 @ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
998 if( g.zLogin!=0 ){
999 @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
1000 @ SQL archive</a></td></tr>
1001 }
1002 fossil_free(zUrl);
1003 blob_reset(&projName);
 
 
 
 
1004 }
1005
1006 @ <tr><th>Timelines:</th><td>
1007 @ %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
1008 if( zParent ){
@@ -1167,15 +1174,16 @@
1167 }
1168 render_backlink_graph(zUuid,
1169 "<div class=\"section accordion\">References</div>\n");
1170 @ <div class="section accordion">Context</div><div class="accordion_panel">
1171 render_checkin_context(rid, 0, 0, 0);
1172 @ </div><div class="section accordion">Changes</div>
1173 @ <div class="accordion_panel">
1174 @ <div class="sectionmenu info-changes-menu">
1175 /* ^^^ .info-changes-menu is used by diff scroll sync */
1176 pCfg = construct_diff_flags(diffType, &DCfg);
 
1177 DCfg.pRe = pRe;
1178 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
1179 if( diffType!=1 ){
1180 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1181 @ Unified&nbsp;Diff</a>
@@ -1227,10 +1235,18 @@
1227 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
1228 pCfg,mperm);
1229 }
1230 db_finalize(&q3);
1231 @ </div>
 
 
 
 
 
 
 
 
1232 append_diff_javascript(diffType);
1233 style_finish_page();
1234 }
1235
1236 /*
@@ -1414,17 +1430,18 @@
1414 Blob qpGlob; /* glob= query parameter for generated links */
1415 int bInvert = PB("inv");
1416
1417 login_check_credentials();
1418 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1419 login_anonymous_available();
1420 fossil_nice_default();
1421 blob_init(&qp, 0, 0);
1422 blob_init(&qpGlob, 0, 0);
1423 diffType = preferred_diff_type();
1424 zRe = P("regex");
1425 if( zRe ) re_compile(&pRe, zRe, 0);
1426 zBranch = P("branch");
1427 if( zBranch && zBranch[0]==0 ) zBranch = 0;
1428 if( zBranch ){
1429 blob_appendf(&qp, "branch=%T", zBranch);
1430 zMergeOrigin = mprintf("merge-in:%s", zBranch);
@@ -1918,18 +1935,31 @@
1918 ** * The "preferred-diff-type" setting
1919 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1920 */
1921 int preferred_diff_type(void){
1922 int dflt;
 
 
1923 static char zDflt[2]
1924 /*static b/c cookie_link_parameter() does not copy it!*/;
1925 dflt = db_get_int("preferred-diff-type",-99);
1926 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
 
 
 
 
 
 
1927 zDflt[0] = dflt + '0';
1928 zDflt[1] = 0;
1929 cookie_link_parameter("diff","diff", zDflt);
1930 return atoi(PD_NoBot("diff",zDflt));
 
 
 
 
 
1931 }
1932
1933
1934 /*
1935 ** WEBPAGE: fdiff
@@ -1967,10 +1997,11 @@
1967 int verbose = PB("verbose");
1968 DiffConfig DCfg;
1969
1970 login_check_credentials();
1971 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1972 diff_config_init(&DCfg, 0);
1973 diffType = preferred_diff_type();
1974 if( P("from") && P("to") ){
1975 v1 = artifact_from_ci_and_filename("from");
1976 v2 = artifact_from_ci_and_filename("to");
@@ -2009,11 +2040,11 @@
2009 }
2010 db_finalize(&q);
2011 }
2012 zRe = P("regex");
2013 cgi_check_for_malice();
2014 if( zRe ) re_compile(&pRe, zRe, 0);
2015 if( verbose ) objdescFlags |= OBJDESC_DETAIL;
2016 if( isPatch ){
2017 Blob c1, c2, *pOut;
2018 DiffConfig DCfg;
2019 pOut = cgi_output_blob();
@@ -2407,15 +2438,15 @@
2407 object_description(rid, objdescFlags, 0, &downloadName);
2408 style_submenu_element("Download", "%R/raw/%s?at=%T",
2409 zUuid, file_tail(blob_str(&downloadName)));
2410 @ <hr>
2411 content_get(rid, &content);
2412 if( !g.isHuman ){
2413 /* Prevent robots from running hexdump on megabyte-sized source files
2414 ** and there by eating up lots of CPU time and bandwidth. There is
2415 ** no good reason for a robot to need a hexdump. */
2416 @ <p>A hex dump of this file is not available.
2417 @ Please download the raw binary file and generate a hex dump yourself.</p>
2418 }else{
2419 @ <blockquote><pre>
2420 hexdump(&content);
2421 @ </pre></blockquote>
@@ -2915,11 +2946,12 @@
2915 @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
2916 }
2917 db_finalize(&q);
2918 }
2919 if( !docOnly ){
2920 style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName));
 
2921 if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
2922 style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
2923 }
2924 }
2925 if( zMime ){
2926
--- src/info.c
+++ src/info.c
@@ -401,34 +401,36 @@
401 @ </span></div>
402 if( pCfg ){
403 append_diff(zOld, zNew, pCfg);
404 }
405 }else{
406 const char *zCkin2 =
407 mprintf(validate16(zCkin, -1) ? "%!S" : "%T"/*works-like:"%s"*/, zCkin);
408 if( zOld && zNew ){
409 if( fossil_strcmp(zOld, zNew)!=0 ){
410 if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
411 @ Renamed and modified
412 @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\
413 @ %h(zOldName)</a>
414 @ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
415 @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
416 @ %h(zName)</a>
417 @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
418 }else{
419 @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
420 @ %h(zName)</a>
421 @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
422 @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
423 }
424 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
425 @ Name change
426 @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\
427 @ %h(zOldName)</a>
428 @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
429 @ %h(zName)</a>.
430 }else{
431 @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
432 @ %h(zName)</a> became
433 if( mperm==PERM_EXE ){
434 @ executable with contents
435 }else if( mperm==PERM_LNK ){
436 @ a symlink with target
@@ -436,14 +438,14 @@
438 @ a regular file with contents
439 }
440 @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
441 }
442 }else if( zOld ){
443 @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zOld,zCkin2))\
444 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
445 }else{
446 @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
447 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
448 }
449 if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
450 if( pCfg ){
451 @ </span></div>
@@ -643,10 +645,12 @@
645 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
646 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
647 }else{
648 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
649 }
650 @ <div class="section" id="changes_section">Changes</div>
651 DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
652 @ <div class="sectionmenu info-changes-menu">
653 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
654 if( diffType!=1 ){
655 @ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
656 }
@@ -718,10 +722,16 @@
722 blob_reset(&old);
723 blob_reset(&new);
724 }
725 }
726 db_finalize(&q);
727 @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
728 @ document.getElementById('changes_section').textContent = 'Changes ' +
729 @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
730 @ '+%d(g.diffCnt[1]) ' +
731 @ '−%d(g.diffCnt[2]))'
732 @ </script>
733 append_diff_javascript(diffType);
734 }
735
736 /*
737 ** Render a web-page diff of the changes in the working check-out to
@@ -741,10 +751,12 @@
751 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
752 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
753 }else{
754 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
755 }
756 @ <div class="section" id="changes_section">Changes</div>
757 DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
758 @ <div class="sectionmenu info-changes-menu">
759 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
760 if( diffType!=1 ){
761 @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
762 @ Unified&nbsp;Diff</a>
@@ -803,10 +815,16 @@
815 }
816 fossil_free(zLhs);
817 fossil_free(zRhs);
818 }
819 db_finalize(&q);
820 @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
821 @ document.getElementById('changes_section').textContent = 'Changes ' +
822 @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
823 @ '+%d(g.diffCnt[1]) ' +
824 @ '−%d(g.diffCnt[2]))'
825 @ </script>
826 append_diff_javascript(diffType);
827 }
828
829 /*
830 ** WEBPAGE: ckout
@@ -918,11 +936,11 @@
936 @ No such object: %h(zName)
937 style_finish_page();
938 return;
939 }
940 zRe = P("regex");
941 if( zRe ) fossil_re_compile(&pRe, zRe, 0);
942 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
943 zParent = db_text(0,
944 "SELECT uuid FROM plink, blob"
945 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
946 rid
@@ -975,34 +993,23 @@
993 @ <tr><th>Comment:</th><td class="infoComment">\
994 @ %!W(zEComment?zEComment:zComment)</td></tr>
995
996 /* The Download: line */
997 if( g.perm.Zip ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998 @ <tr><th>Downloads:</th><td>
999 if( robot_would_be_restricted("download") ){
1000 @ See separate %z(href("%R/rchvdwnld/%!S",zUuid))download page</a>
1001 }else{
1002 char *zBase = archive_base_name(rid);
1003 @ %z(href("%R/tarball/%s.tar.gz",zBase))Tarball</a>
1004 @ | %z(href("%R/zip/%s.zip",zBase))ZIP archive</a>
1005 if( g.zLogin!=0 ){
1006 @ | %z(href("%R/sqlar/%s.sqlar",zBase))\
1007 @ SQL archive</a></td></tr>
1008 }
1009 fossil_free(zBase);
1010 }
1011 }
1012
1013 @ <tr><th>Timelines:</th><td>
1014 @ %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
1015 if( zParent ){
@@ -1167,15 +1174,16 @@
1174 }
1175 render_backlink_graph(zUuid,
1176 "<div class=\"section accordion\">References</div>\n");
1177 @ <div class="section accordion">Context</div><div class="accordion_panel">
1178 render_checkin_context(rid, 0, 0, 0);
1179 @ </div><div class="section accordion" id="changes_section">Changes</div>
1180 @ <div class="accordion_panel">
1181 @ <div class="sectionmenu info-changes-menu">
1182 /* ^^^ .info-changes-menu is used by diff scroll sync */
1183 pCfg = construct_diff_flags(diffType, &DCfg);
1184 DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
1185 DCfg.pRe = pRe;
1186 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
1187 if( diffType!=1 ){
1188 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1189 @ Unified&nbsp;Diff</a>
@@ -1227,10 +1235,18 @@
1235 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
1236 pCfg,mperm);
1237 }
1238 db_finalize(&q3);
1239 @ </div>
1240 if( diffType!=0 ){
1241 @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
1242 @ document.getElementById('changes_section').textContent = 'Changes ' +
1243 @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
1244 @ '+%d(g.diffCnt[1]) ' +
1245 @ '−%d(g.diffCnt[2]))'
1246 @ </script>
1247 }
1248 append_diff_javascript(diffType);
1249 style_finish_page();
1250 }
1251
1252 /*
@@ -1414,17 +1430,18 @@
1430 Blob qpGlob; /* glob= query parameter for generated links */
1431 int bInvert = PB("inv");
1432
1433 login_check_credentials();
1434 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1435 if( robot_restrict("diff") ) return;
1436 login_anonymous_available();
1437 fossil_nice_default();
1438 blob_init(&qp, 0, 0);
1439 blob_init(&qpGlob, 0, 0);
1440 diffType = preferred_diff_type();
1441 zRe = P("regex");
1442 if( zRe ) fossil_re_compile(&pRe, zRe, 0);
1443 zBranch = P("branch");
1444 if( zBranch && zBranch[0]==0 ) zBranch = 0;
1445 if( zBranch ){
1446 blob_appendf(&qp, "branch=%T", zBranch);
1447 zMergeOrigin = mprintf("merge-in:%s", zBranch);
@@ -1918,18 +1935,31 @@
1935 ** * The "preferred-diff-type" setting
1936 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1937 */
1938 int preferred_diff_type(void){
1939 int dflt;
1940 int res;
1941 int isBot;
1942 static char zDflt[2]
1943 /*static b/c cookie_link_parameter() does not copy it!*/;
1944 if( robot_would_be_restricted("diff") ){
1945 dflt = 0;
1946 isBot = 1;
1947 }else{
1948 dflt = db_get_int("preferred-diff-type",-99);
1949 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1950 isBot = 0;
1951 }
1952 zDflt[0] = dflt + '0';
1953 zDflt[1] = 0;
1954 cookie_link_parameter("diff","diff", zDflt);
1955 res = atoi(PD_NoBot("diff",zDflt));
1956 if( isBot && res>0 && robot_restrict("diff") ){
1957 cgi_reply();
1958 fossil_exit(0);
1959 }
1960 return res;
1961 }
1962
1963
1964 /*
1965 ** WEBPAGE: fdiff
@@ -1967,10 +1997,11 @@
1997 int verbose = PB("verbose");
1998 DiffConfig DCfg;
1999
2000 login_check_credentials();
2001 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2002 if( robot_restrict("diff") ) return;
2003 diff_config_init(&DCfg, 0);
2004 diffType = preferred_diff_type();
2005 if( P("from") && P("to") ){
2006 v1 = artifact_from_ci_and_filename("from");
2007 v2 = artifact_from_ci_and_filename("to");
@@ -2009,11 +2040,11 @@
2040 }
2041 db_finalize(&q);
2042 }
2043 zRe = P("regex");
2044 cgi_check_for_malice();
2045 if( zRe ) fossil_re_compile(&pRe, zRe, 0);
2046 if( verbose ) objdescFlags |= OBJDESC_DETAIL;
2047 if( isPatch ){
2048 Blob c1, c2, *pOut;
2049 DiffConfig DCfg;
2050 pOut = cgi_output_blob();
@@ -2407,15 +2438,15 @@
2438 object_description(rid, objdescFlags, 0, &downloadName);
2439 style_submenu_element("Download", "%R/raw/%s?at=%T",
2440 zUuid, file_tail(blob_str(&downloadName)));
2441 @ <hr>
2442 content_get(rid, &content);
2443 if( blob_size(&content)>100000 ){
2444 /* Prevent robots from running hexdump on megabyte-sized source files
2445 ** and there by eating up lots of CPU time and bandwidth. There is
2446 ** no good reason for a robot to need a hexdump. */
2447 @ <p>A hex dump of this file is not available because it is too large.
2448 @ Please download the raw binary file and generate a hex dump yourself.</p>
2449 }else{
2450 @ <blockquote><pre>
2451 hexdump(&content);
2452 @ </pre></blockquote>
@@ -2915,11 +2946,12 @@
2946 @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
2947 }
2948 db_finalize(&q);
2949 }
2950 if( !docOnly ){
2951 style_submenu_element("Download", "%R/raw/%s?at=%T",
2952 zUuid, file_tail(blob_str(&downloadName)));
2953 if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
2954 style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
2955 }
2956 }
2957 if( zMime ){
2958
+1 -1
--- src/json.c
+++ src/json.c
@@ -78,11 +78,11 @@
7878
** whether or not we're really running in json mode we have to try
7979
** a bit harder. Problem reported here:
8080
** https://fossil-scm.org/forum/forumpost/e4953666d6
8181
*/
8282
ReCompiled * pReg = 0;
83
- const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
83
+ const char * zErr = fossil_re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
8484
assert(zErr==0 && "Regex compilation failed?");
8585
if(zErr==0 &&
8686
re_match(pReg, (const unsigned char *)zPathInfo, -1)){
8787
rc = 2;
8888
}
8989
--- src/json.c
+++ src/json.c
@@ -78,11 +78,11 @@
78 ** whether or not we're really running in json mode we have to try
79 ** a bit harder. Problem reported here:
80 ** https://fossil-scm.org/forum/forumpost/e4953666d6
81 */
82 ReCompiled * pReg = 0;
83 const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
84 assert(zErr==0 && "Regex compilation failed?");
85 if(zErr==0 &&
86 re_match(pReg, (const unsigned char *)zPathInfo, -1)){
87 rc = 2;
88 }
89
--- src/json.c
+++ src/json.c
@@ -78,11 +78,11 @@
78 ** whether or not we're really running in json mode we have to try
79 ** a bit harder. Problem reported here:
80 ** https://fossil-scm.org/forum/forumpost/e4953666d6
81 */
82 ReCompiled * pReg = 0;
83 const char * zErr = fossil_re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
84 assert(zErr==0 && "Regex compilation failed?");
85 if(zErr==0 &&
86 re_match(pReg, (const unsigned char *)zPathInfo, -1)){
87 rc = 2;
88 }
89
+131 -142
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160160
161161
if( zUsername==0 ) return 0;
162162
else if( zPassword==0 ) return 0;
163163
else if( zCS==0 ) return 0;
164164
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
165
+ else if( anon_cookie_lifespan()==0 ) return 0;
165166
while( 1/*exit-by-break*/ ){
166167
zPw = captcha_decode((unsigned int)atoi(zCS), n);
167168
if( zPw==0 ) return 0;
168169
if( fossil_stricmp(zPw, zPassword)==0 ) break;
169170
n++;
@@ -338,33 +339,62 @@
338339
*zDest = zCookie;
339340
}else{
340341
free(zCookie);
341342
}
342343
}
344
+
345
+/*
346
+** SETTING: anon-cookie-lifespan width=10 default=480
347
+** The number of minutes for which an anonymous login cookie is
348
+** valid. Anonymous logins are prohibited if this value is zero.
349
+*/
350
+
351
+
352
+/*
353
+** The default lifetime of an anoymous cookie, in minutes.
354
+*/
355
+#define ANONYMOUS_COOKIE_LIFESPAN (8*60)
356
+
357
+/*
358
+** Return the lifetime of an anonymous cookie, in minutes.
359
+*/
360
+int anon_cookie_lifespan(void){
361
+ static int lifespan = -1;
362
+ if( lifespan<0 ){
363
+ lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
364
+ if( lifespan<0 ) lifespan = 0;
365
+ }
366
+ return lifespan;
367
+}
343368
344369
/* Sets a cookie for an anonymous user login, which looks like this:
345370
**
346371
** HASH/TIME/anonymous
347372
**
348
-** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
373
+** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET
374
+** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value.
349375
**
350376
** If zCookieDest is not NULL then the generated cookie is assigned to
351377
** *zCookieDest and the caller must eventually free() it.
352378
**
353379
** If bSessionCookie is true, the cookie will be a session cookie.
380
+**
381
+** Search for tag-20250817a to find the code that recognizes this cookie.
354382
*/
355383
void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
356384
char *zNow; /* Current time (julian day number) */
357385
char *zCookie; /* The login cookie */
386
+ const char *zUserAgent; /* The user agent */
358387
const char *zCookieName; /* Name of the login cookie */
359388
Blob b; /* Blob used during cookie construction */
360
- int expires = bSessionCookie ? 0 : 6*3600;
389
+ int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
361390
zCookieName = login_cookie_name();
362391
zNow = db_text("0", "SELECT julianday('now')");
363392
assert( zCookieName && zNow );
364393
blob_init(&b, zNow, -1);
365
- blob_appendf(&b, "/%z", captcha_secret(0));
394
+ zUserAgent = PD("HTTP_USER_AGENT","nil");
395
+ blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0));
366396
sha1sum_blob(&b, &b);
367397
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
368398
blob_reset(&b);
369399
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
370400
if( zCookieDest ){
@@ -581,17 +611,35 @@
581611
/* If the "Reset Password" button in the form was pressed, render
582612
** the Request Password Reset page in place of this one. */
583613
login_reqpwreset_page();
584614
return;
585615
}
586
- login_check_credentials();
616
+
617
+ /* If the "anon" query parameter is 1 or 2, that means rework the web-page
618
+ ** to make it a more user-friendly captcha. Extraneous text and boxes
619
+ ** are omitted. The user has just the captcha image and an entry box
620
+ ** and a "Verify" button. Underneath is the same login page for user
621
+ ** "anonymous", just displayed in an easier to digest format for one-time
622
+ ** visitors.
623
+ **
624
+ ** anon=1 is advisory and only has effect if there is not some other login
625
+ ** cookie. anon=2 means always show the captcha.
626
+ */
627
+ anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
628
+ if( anonFlag==2 ){
629
+ g.zLogin = 0;
630
+ }else{
631
+ login_check_credentials();
632
+ if( g.zLogin!=0 ) anonFlag = 0;
633
+ }
634
+
587635
fossil_redirect_to_https_if_needed(1);
588636
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
589637
constant_time_cmp_function, 0, 0);
590638
zUsername = P("u");
591639
zPasswd = P("p");
592
- anonFlag = g.zLogin==0 && PB("anon");
640
+
593641
/* Handle log-out requests */
594642
if( P("out") && cgi_csrf_safe(2) ){
595643
login_clear_login_data();
596644
login_redirect_to_g();
597645
return;
@@ -717,10 +765,11 @@
717765
login_redirect_to_g();
718766
}
719767
}
720768
style_set_current_feature("login");
721769
style_header("Login/Logout");
770
+ if( anonFlag==2 ) g.zLogin = 0;
722771
style_adunit_config(ADUNIT_OFF);
723772
@ %s(zErrMsg)
724773
if( zGoto && !noAnon ){
725774
char *zAbbrev = fossil_strdup(zGoto);
726775
int i;
@@ -728,12 +777,12 @@
728777
zAbbrev[i] = 0;
729778
if( g.zLogin ){
730779
@ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
731780
@ to access <b>%h(zAbbrev)</b>.
732781
}else if( anonFlag ){
733
- @ <p>Login as <b>anonymous</b> or any named user
734
- @ to access page <b>%h(zAbbrev)</b>.
782
+ @ <p><b>Verify that you are human by typing in the 8-character text
783
+ @ password shown below.</b></p>
735784
}else{
736785
@ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
737786
}
738787
fossil_free(zAbbrev);
739788
}
@@ -748,26 +797,27 @@
748797
if( zGoto ){
749798
@ <input type="hidden" name="g" value="%h(zGoto)">
750799
}
751800
if( anonFlag ){
752801
@ <input type="hidden" name="anon" value="1">
802
+ @ <input type="hidden" name="u" value="anonymous">
753803
}
754804
if( g.zLogin ){
755805
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
756806
@ <input type="submit" name="out" value="Logout" autofocus></p>
757807
@ </form>
758808
}else{
759809
unsigned int uSeed = captcha_seed();
760
- if( g.zLogin==0 && (anonFlag || zGoto==0) ){
810
+ if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
761811
zAnonPw = db_text(0, "SELECT pw FROM user"
762812
" WHERE login='anonymous'"
763813
" AND cap!=''");
764814
}else{
765815
zAnonPw = 0;
766816
}
767817
@ <table class="login_out">
768
- if( P("HTTPS")==0 ){
818
+ if( P("HTTPS")==0 && !anonFlag ){
769819
@ <tr><td class="form_label">Warning:</td>
770820
@ <td><span class='securityWarning'>
771821
@ Login information, including the password,
772822
@ will be sent in the clear over an unencrypted connection.
773823
if( !g.sslNotAvailable ){
@@ -774,41 +824,55 @@
774824
@ Consider logging in at
775825
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
776826
}
777827
@ </span></td></tr>
778828
}
779
- @ <tr>
780
- @ <td class="form_label" id="userlabel1">User ID:</td>
781
- @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
782
- @ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td>
783
- @ </tr>
829
+ if( !anonFlag ){
830
+ @ <tr>
831
+ @ <td class="form_label" id="userlabel1">User ID:</td>
832
+ @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
833
+ @ size="30" value="" autofocus></td>
834
+ @ </tr>
835
+ }
784836
@ <tr>
785837
@ <td class="form_label" id="pswdlabel">Password:</td>
786838
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
787
- @ name="p" value="" size="30">\
788
- if( zAnonPw && !noAnon ){
839
+ @ name="p" value="" size="30"%s(anonFlag ? " autofocus" : "")>
840
+ if( anonFlag ){
841
+ @ </td></tr>
842
+ @ <tr>
843
+ @ <td></td><td>\
844
+ captcha_speakit_button(uSeed, "Read the password out loud");
845
+ }else if( zAnonPw && !noAnon ){
789846
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
790847
}
791848
@ </td>
792849
@ </tr>
793
- @ <tr>
794
- @ <td></td>
795
- @ <td><input type="checkbox" name="remember" value="1" \
796
- @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
797
- @ <label for="remember-me">Remember me?</label></td>
798
- @ </tr>
799
- @ <tr>
800
- @ <td></td>
801
- @ <td><input type="submit" name="in" value="Login">
802
- @ </tr>
803
- if( !noAnon && login_self_register_available(0) ){
850
+ if( !anonFlag ){
851
+ @ <tr>
852
+ @ <td></td>
853
+ @ <td><input type="checkbox" name="remember" value="1" \
854
+ @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
855
+ @ <label for="remember-me">Remember me?</label></td>
856
+ @ </tr>
857
+ @ <tr>
858
+ @ <td></td>
859
+ @ <td><input type="submit" name="in" value="Login">
860
+ @ </tr>
861
+ }else{
862
+ @ <tr>
863
+ @ <td></td>
864
+ @ <td><input type="submit" name="in" value="Verify that I am human">
865
+ @ </tr>
866
+ }
867
+ if( !anonFlag && !noAnon && login_self_register_available(0) ){
804868
@ <tr>
805869
@ <td></td>
806870
@ <td><input type="submit" name="self" value="Create A New Account">
807871
@ </tr>
808872
}
809
- if( login_self_password_reset_available() ){
873
+ if( !anonFlag && login_self_password_reset_available() ){
810874
@ <tr>
811875
@ <td></td>
812876
@ <td><input type="submit" name="pwreset" value="Reset My Password">
813877
@ </tr>
814878
}
@@ -817,27 +881,29 @@
817881
const char *zDecoded = captcha_decode(uSeed, 0);
818882
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
819883
char *zCaptcha = captcha_render(zDecoded);
820884
821885
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
822
- @ Visitors may enter <b>anonymous</b> as the user-ID with
823
- @ the 8-character hexadecimal password shown below:</p>
886
+ if( !anonFlag ){
887
+ @ Visitors may enter <b>anonymous</b> as the user-ID with
888
+ @ the 8-character hexadecimal password shown below:</p>
889
+ }
824890
@ <div class="captcha"><table class="captcha"><tr><td>\
825891
@ <pre class="captcha">
826892
@ %h(zCaptcha)
827893
@ </pre></td></tr></table>
828
- if( bAutoCaptcha ) {
894
+ if( bAutoCaptcha && !anonFlag ) {
829895
@ <input type="button" value="Fill out captcha" id='autofillButton' \
830896
@ data-af='%s(zDecoded)'>
831897
builtin_request_js("login.js");
832898
}
833899
@ </div>
834900
free(zCaptcha);
835901
}
836902
@ </form>
837903
}
838
- if( login_is_individual() ){
904
+ if( login_is_individual() && !anonFlag ){
839905
if( g.perm.EmailAlert && alert_enabled() ){
840906
@ <hr>
841907
@ <p>Configure <a href="%R/alerts">Email Alerts</a>
842908
@ for user <b>%h(g.zLogin)</b></p>
843909
}
@@ -845,15 +911,18 @@
845911
@ <hr><p>
846912
@ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
847913
@ post timeline</a> for user <b>%h(g.zLogin)</b></p>
848914
}
849915
}
850
- @ <hr><p>
851
- @ Select your preferred <a href="%R/skins">site skin</a>.
852
- @ </p>
853
- @ <hr><p>
854
- @ Manage your <a href="%R/cookies">cookies</a>.</p>
916
+ if( !anonFlag ){
917
+ @ <hr><p>
918
+ @ Select your preferred <a href="%R/skins">site skin</a>.
919
+ @ </p>
920
+ @ <hr><p>
921
+ @ Manage your <a href="%R/cookies">cookies</a> or your
922
+ @ <a href="%R/tokens">access tokens</a>.</p>
923
+ }
855924
if( login_is_individual() ){
856925
if( g.perm.Password ){
857926
char *zRPW = fossil_random_password(12);
858927
@ <hr>
859928
@ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
@@ -1262,98 +1331,10 @@
12621331
}
12631332
fossil_free(zDecode);
12641333
return uid;
12651334
}
12661335
1267
-/*
1268
-** SETTING: robot-restrict width=40 block-text
1269
-** The VALUE of this setting is a list of GLOB patterns that match
1270
-** pages for which complex HTTP requests from robots should be disallowed.
1271
-** The recommended value for this setting is:
1272
-**
1273
-** timeline,vdiff,fdiff,annotate,blame
1274
-**
1275
-*/
1276
-
1277
-/*
1278
-** Check to see if the current HTTP request is a complex request that
1279
-** is coming from a robot and if access should restricted for such robots.
1280
-** For the purposes of this module, a "complex request" is an HTTP
1281
-** request with one or more query parameters other than "name".
1282
-**
1283
-** If this routine determines that robots should be restricted, then
1284
-** this routine publishes a redirect to the honeypot and exits without
1285
-** returning to the caller.
1286
-**
1287
-** This routine believes that this is a complex request is coming from
1288
-** a robot if all of the following are true:
1289
-**
1290
-** * The user is "nobody".
1291
-** * Either the REFERER field of the HTTP header is missing or empty,
1292
-** or the USERAGENT field of the HTTP header suggests that
1293
-** the request as coming from a robot.
1294
-** * There are one or more query parameters other than "name".
1295
-**
1296
-** Robot restrictions are governed by settings.
1297
-**
1298
-** robot-restrict The value is a list of GLOB patterns for pages
1299
-** that should restrict robot access. No restrictions
1300
-** are applied if this setting is undefined or is
1301
-** an empty string.
1302
-*/
1303
-void login_restrict_robot_access(void){
1304
- const char *zGlob;
1305
- int isMatch = 1;
1306
- int nQP; /* Number of query parameters other than name= */
1307
- if( g.zLogin!=0 ) return;
1308
- zGlob = db_get("robot-restrict",0);
1309
- if( zGlob==0 || zGlob[0]==0 ) return;
1310
- if( g.isHuman ){
1311
- const char *zReferer;
1312
- const char *zAccept;
1313
- const char *zBr;
1314
- zReferer = P("HTTP_REFERER");
1315
- if( zReferer && zReferer[0]!=0 ) return;
1316
-
1317
- /* Robots typically do not accept the brotli encoding, at least not
1318
- ** at the time of this writing (2025-04-01), but standard web-browser
1319
- ** all generally do accept brotli. So if brotli is accepted,
1320
- ** assume we are not talking to a robot. We might want to revisit this
1321
- ** heuristic in the future...
1322
- */
1323
- if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
1324
- && (zBr = strstr(zAccept,"br"))!=0
1325
- && !fossil_isalnum(zBr[2])
1326
- && (zBr==zAccept || !fossil_isalnum(zBr[-1]))
1327
- ){
1328
- return;
1329
- }
1330
- }
1331
- nQP = cgi_qp_count();
1332
- if( nQP<1 ) return;
1333
- isMatch = glob_multi_match(zGlob, g.zPath);
1334
- if( !isMatch ) return;
1335
-
1336
- /* Check for exceptions to the restriction on the number of query
1337
- ** parameters. */
1338
- zGlob = db_get("robot-restrict-qp",0);
1339
- if( zGlob && zGlob[0] ){
1340
- char *zPath = mprintf("%s/%d", g.zPath, nQP);
1341
- isMatch = glob_multi_match(zGlob, zPath);
1342
- fossil_free(zPath);
1343
- if( isMatch ) return;
1344
- }
1345
-
1346
- /* If we reach this point, it means we have a situation where we
1347
- ** want to restrict the activity of a robot.
1348
- */
1349
- g.isHuman = 0;
1350
- (void)exclude_spiders(0);
1351
- cgi_reply();
1352
- fossil_exit(0);
1353
-}
1354
-
13551336
/*
13561337
** When this routine is called, we know that the request does not
13571338
** have a login on the present repository. This routine checks to
13581339
** see if their login cookie might be for another member of the
13591340
** login-group.
@@ -1388,11 +1369,11 @@
13881369
**
13891370
** g.userUid Database USER.UID value. Might be -1 for "nobody"
13901371
** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
13911372
** g.perm Permissions granted to this user
13921373
** g.anon Permissions that would be available to anonymous
1393
-** g.isHuman True if the user is human, not a spider or robot
1374
+** g.isRobot True if the client is known to be a spider or robot
13941375
** g.perm Populated based on user account's capabilities
13951376
**
13961377
*/
13971378
void login_check_credentials(void){
13981379
int uid = 0; /* User id */
@@ -1429,11 +1410,11 @@
14291410
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
14301411
}
14311412
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
14321413
zCap = "sxy";
14331414
g.noPswd = 1;
1434
- g.isHuman = 1;
1415
+ g.isRobot = 0;
14351416
zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
14361417
" FROM user WHERE uid=%d", uid);
14371418
login_create_csrf_secret(zSeed);
14381419
fossil_free(zSeed);
14391420
}
@@ -1457,33 +1438,38 @@
14571438
}
14581439
}
14591440
}
14601441
if( zUser==0 ){
14611442
/* Invalid cookie */
1462
- }else if( fossil_strcmp(zUser, "anonymous")==0 ){
1463
- /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
1464
- ** too old and the sha1 hash of TIME/SECRET must match HASH.
1465
- ** SECRET is the "captcha-secret" value in the repository.
1443
+ }else if( fossil_strcmp(zUser, "anonymous")==0
1444
+ && anon_cookie_lifespan()>0 ){
1445
+ /* Cookies of the form "HASH/TIME/anonymous". The TIME must
1446
+ ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
1447
+ ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
1448
+ ** is the HTTP_USER_AGENT of the client and SECRET is the
1449
+ ** "captcha-secret" value in the repository. See tag-20250817a
1450
+ ** for the code the creates this cookie.
14661451
*/
14671452
double rTime = atof(zArg);
1453
+ const char *zUserAgent = PD("HTTP_USER_AGENT","nil");
14681454
Blob b;
14691455
char *zSecret;
14701456
int n = 0;
14711457
14721458
do{
14731459
blob_zero(&b);
14741460
zSecret = captcha_secret(n++);
14751461
if( zSecret==0 ) break;
1476
- blob_appendf(&b, "%s/%s", zArg, zSecret);
1462
+ blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret);
14771463
sha1sum_blob(&b, &b);
14781464
if( fossil_strcmp(zHash, blob_str(&b))==0 ){
14791465
uid = db_int(0,
14801466
"SELECT uid FROM user WHERE login='anonymous'"
14811467
" AND octet_length(cap)>0"
14821468
" AND octet_length(pw)>0"
1483
- " AND %.17g+0.25>julianday('now')",
1484
- rTime
1469
+ " AND %.17g>julianday('now')",
1470
+ rTime+anon_cookie_lifespan()/1440.0
14851471
);
14861472
}
14871473
}while( uid==0 );
14881474
blob_reset(&b);
14891475
}else{
@@ -1559,12 +1545,15 @@
15591545
login_create_csrf_secret("none");
15601546
}
15611547
15621548
login_set_uid(uid, zCap);
15631549
1564
- /* Maybe restrict access to robots */
1565
- login_restrict_robot_access();
1550
+ /* Maybe restrict access by robots */
1551
+ if( g.zLogin==0 && robot_restrict(g.zPath) ){
1552
+ cgi_reply();
1553
+ fossil_exit(0);
1554
+ }
15661555
}
15671556
15681557
/*
15691558
** Set the current logged in user to be uid. zCap is precomputed
15701559
** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1599,15 +1588,15 @@
15991588
g.userUid = uid;
16001589
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
16011590
g.zLogin = 0;
16021591
}
16031592
if( PB("isrobot") ){
1604
- g.isHuman = 0;
1593
+ g.isRobot = 1;
16051594
}else if( g.zLogin==0 ){
1606
- g.isHuman = isHuman(P("HTTP_USER_AGENT"));
1595
+ g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
16071596
}else{
1608
- g.isHuman = 1;
1597
+ g.isRobot = 0;
16091598
}
16101599
16111600
/* Set the capabilities */
16121601
login_replace_capabilities(zCap, 0);
16131602
@@ -1617,11 +1606,11 @@
16171606
** enabled for this repository and make appropriate adjustments to the
16181607
** permission flags if it is. This should be done before the permissions
16191608
** are (potentially) copied to the anonymous permission set; otherwise,
16201609
** those will be out-of-sync.
16211610
*/
1622
- if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
1611
+ if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
16231612
int autoLink = db_get_int("auto-hyperlink",1);
16241613
if( autoLink==1 ){
16251614
g.jsHref = 1;
16261615
g.perm.Hyperlink = 1;
16271616
}else if( autoLink==2 ){
@@ -1927,11 +1916,11 @@
19271916
blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
19281917
}
19291918
if( zQS && zQS[0] ){
19301919
blob_appendf(&redir, "%%3f%T", zQS);
19311920
}
1932
- if( anonOk ) blob_append(&redir, "&anon", 5);
1921
+ if( anonOk ) blob_append(&redir, "&anon=1", 7);
19331922
cgi_redirect(blob_str(&redir));
19341923
/* NOTREACHED */
19351924
assert(0);
19361925
}
19371926
}
@@ -1941,11 +1930,11 @@
19411930
** the anonymous user has Hyperlink permission, then paint a mesage
19421931
** to inform the user that much more information is available by
19431932
** logging in as anonymous.
19441933
*/
19451934
void login_anonymous_available(void){
1946
- if( !g.perm.Hyperlink && g.anon.Hyperlink ){
1935
+ if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
19471936
const char *zUrl = PD("PATH_INFO", "");
19481937
@ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
19491938
@ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
19501939
@ to enable hyperlinks.</p>
19511940
}
19521941
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160
161 if( zUsername==0 ) return 0;
162 else if( zPassword==0 ) return 0;
163 else if( zCS==0 ) return 0;
164 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
 
165 while( 1/*exit-by-break*/ ){
166 zPw = captcha_decode((unsigned int)atoi(zCS), n);
167 if( zPw==0 ) return 0;
168 if( fossil_stricmp(zPw, zPassword)==0 ) break;
169 n++;
@@ -338,33 +339,62 @@
338 *zDest = zCookie;
339 }else{
340 free(zCookie);
341 }
342 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
344 /* Sets a cookie for an anonymous user login, which looks like this:
345 **
346 ** HASH/TIME/anonymous
347 **
348 ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
 
349 **
350 ** If zCookieDest is not NULL then the generated cookie is assigned to
351 ** *zCookieDest and the caller must eventually free() it.
352 **
353 ** If bSessionCookie is true, the cookie will be a session cookie.
 
 
354 */
355 void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
356 char *zNow; /* Current time (julian day number) */
357 char *zCookie; /* The login cookie */
 
358 const char *zCookieName; /* Name of the login cookie */
359 Blob b; /* Blob used during cookie construction */
360 int expires = bSessionCookie ? 0 : 6*3600;
361 zCookieName = login_cookie_name();
362 zNow = db_text("0", "SELECT julianday('now')");
363 assert( zCookieName && zNow );
364 blob_init(&b, zNow, -1);
365 blob_appendf(&b, "/%z", captcha_secret(0));
 
366 sha1sum_blob(&b, &b);
367 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
368 blob_reset(&b);
369 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
370 if( zCookieDest ){
@@ -581,17 +611,35 @@
581 /* If the "Reset Password" button in the form was pressed, render
582 ** the Request Password Reset page in place of this one. */
583 login_reqpwreset_page();
584 return;
585 }
586 login_check_credentials();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587 fossil_redirect_to_https_if_needed(1);
588 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
589 constant_time_cmp_function, 0, 0);
590 zUsername = P("u");
591 zPasswd = P("p");
592 anonFlag = g.zLogin==0 && PB("anon");
593 /* Handle log-out requests */
594 if( P("out") && cgi_csrf_safe(2) ){
595 login_clear_login_data();
596 login_redirect_to_g();
597 return;
@@ -717,10 +765,11 @@
717 login_redirect_to_g();
718 }
719 }
720 style_set_current_feature("login");
721 style_header("Login/Logout");
 
722 style_adunit_config(ADUNIT_OFF);
723 @ %s(zErrMsg)
724 if( zGoto && !noAnon ){
725 char *zAbbrev = fossil_strdup(zGoto);
726 int i;
@@ -728,12 +777,12 @@
728 zAbbrev[i] = 0;
729 if( g.zLogin ){
730 @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
731 @ to access <b>%h(zAbbrev)</b>.
732 }else if( anonFlag ){
733 @ <p>Login as <b>anonymous</b> or any named user
734 @ to access page <b>%h(zAbbrev)</b>.
735 }else{
736 @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
737 }
738 fossil_free(zAbbrev);
739 }
@@ -748,26 +797,27 @@
748 if( zGoto ){
749 @ <input type="hidden" name="g" value="%h(zGoto)">
750 }
751 if( anonFlag ){
752 @ <input type="hidden" name="anon" value="1">
 
753 }
754 if( g.zLogin ){
755 @ <p>Currently logged in as <b>%h(g.zLogin)</b>.
756 @ <input type="submit" name="out" value="Logout" autofocus></p>
757 @ </form>
758 }else{
759 unsigned int uSeed = captcha_seed();
760 if( g.zLogin==0 && (anonFlag || zGoto==0) ){
761 zAnonPw = db_text(0, "SELECT pw FROM user"
762 " WHERE login='anonymous'"
763 " AND cap!=''");
764 }else{
765 zAnonPw = 0;
766 }
767 @ <table class="login_out">
768 if( P("HTTPS")==0 ){
769 @ <tr><td class="form_label">Warning:</td>
770 @ <td><span class='securityWarning'>
771 @ Login information, including the password,
772 @ will be sent in the clear over an unencrypted connection.
773 if( !g.sslNotAvailable ){
@@ -774,41 +824,55 @@
774 @ Consider logging in at
775 @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
776 }
777 @ </span></td></tr>
778 }
779 @ <tr>
780 @ <td class="form_label" id="userlabel1">User ID:</td>
781 @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
782 @ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td>
783 @ </tr>
 
 
784 @ <tr>
785 @ <td class="form_label" id="pswdlabel">Password:</td>
786 @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
787 @ name="p" value="" size="30">\
788 if( zAnonPw && !noAnon ){
 
 
 
 
 
789 captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
790 }
791 @ </td>
792 @ </tr>
793 @ <tr>
794 @ <td></td>
795 @ <td><input type="checkbox" name="remember" value="1" \
796 @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
797 @ <label for="remember-me">Remember me?</label></td>
798 @ </tr>
799 @ <tr>
800 @ <td></td>
801 @ <td><input type="submit" name="in" value="Login">
802 @ </tr>
803 if( !noAnon && login_self_register_available(0) ){
 
 
 
 
 
 
 
804 @ <tr>
805 @ <td></td>
806 @ <td><input type="submit" name="self" value="Create A New Account">
807 @ </tr>
808 }
809 if( login_self_password_reset_available() ){
810 @ <tr>
811 @ <td></td>
812 @ <td><input type="submit" name="pwreset" value="Reset My Password">
813 @ </tr>
814 }
@@ -817,27 +881,29 @@
817 const char *zDecoded = captcha_decode(uSeed, 0);
818 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
819 char *zCaptcha = captcha_render(zDecoded);
820
821 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
822 @ Visitors may enter <b>anonymous</b> as the user-ID with
823 @ the 8-character hexadecimal password shown below:</p>
 
 
824 @ <div class="captcha"><table class="captcha"><tr><td>\
825 @ <pre class="captcha">
826 @ %h(zCaptcha)
827 @ </pre></td></tr></table>
828 if( bAutoCaptcha ) {
829 @ <input type="button" value="Fill out captcha" id='autofillButton' \
830 @ data-af='%s(zDecoded)'>
831 builtin_request_js("login.js");
832 }
833 @ </div>
834 free(zCaptcha);
835 }
836 @ </form>
837 }
838 if( login_is_individual() ){
839 if( g.perm.EmailAlert && alert_enabled() ){
840 @ <hr>
841 @ <p>Configure <a href="%R/alerts">Email Alerts</a>
842 @ for user <b>%h(g.zLogin)</b></p>
843 }
@@ -845,15 +911,18 @@
845 @ <hr><p>
846 @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
847 @ post timeline</a> for user <b>%h(g.zLogin)</b></p>
848 }
849 }
850 @ <hr><p>
851 @ Select your preferred <a href="%R/skins">site skin</a>.
852 @ </p>
853 @ <hr><p>
854 @ Manage your <a href="%R/cookies">cookies</a>.</p>
 
 
 
855 if( login_is_individual() ){
856 if( g.perm.Password ){
857 char *zRPW = fossil_random_password(12);
858 @ <hr>
859 @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
@@ -1262,98 +1331,10 @@
1262 }
1263 fossil_free(zDecode);
1264 return uid;
1265 }
1266
1267 /*
1268 ** SETTING: robot-restrict width=40 block-text
1269 ** The VALUE of this setting is a list of GLOB patterns that match
1270 ** pages for which complex HTTP requests from robots should be disallowed.
1271 ** The recommended value for this setting is:
1272 **
1273 ** timeline,vdiff,fdiff,annotate,blame
1274 **
1275 */
1276
1277 /*
1278 ** Check to see if the current HTTP request is a complex request that
1279 ** is coming from a robot and if access should restricted for such robots.
1280 ** For the purposes of this module, a "complex request" is an HTTP
1281 ** request with one or more query parameters other than "name".
1282 **
1283 ** If this routine determines that robots should be restricted, then
1284 ** this routine publishes a redirect to the honeypot and exits without
1285 ** returning to the caller.
1286 **
1287 ** This routine believes that this is a complex request is coming from
1288 ** a robot if all of the following are true:
1289 **
1290 ** * The user is "nobody".
1291 ** * Either the REFERER field of the HTTP header is missing or empty,
1292 ** or the USERAGENT field of the HTTP header suggests that
1293 ** the request as coming from a robot.
1294 ** * There are one or more query parameters other than "name".
1295 **
1296 ** Robot restrictions are governed by settings.
1297 **
1298 ** robot-restrict The value is a list of GLOB patterns for pages
1299 ** that should restrict robot access. No restrictions
1300 ** are applied if this setting is undefined or is
1301 ** an empty string.
1302 */
1303 void login_restrict_robot_access(void){
1304 const char *zGlob;
1305 int isMatch = 1;
1306 int nQP; /* Number of query parameters other than name= */
1307 if( g.zLogin!=0 ) return;
1308 zGlob = db_get("robot-restrict",0);
1309 if( zGlob==0 || zGlob[0]==0 ) return;
1310 if( g.isHuman ){
1311 const char *zReferer;
1312 const char *zAccept;
1313 const char *zBr;
1314 zReferer = P("HTTP_REFERER");
1315 if( zReferer && zReferer[0]!=0 ) return;
1316
1317 /* Robots typically do not accept the brotli encoding, at least not
1318 ** at the time of this writing (2025-04-01), but standard web-browser
1319 ** all generally do accept brotli. So if brotli is accepted,
1320 ** assume we are not talking to a robot. We might want to revisit this
1321 ** heuristic in the future...
1322 */
1323 if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
1324 && (zBr = strstr(zAccept,"br"))!=0
1325 && !fossil_isalnum(zBr[2])
1326 && (zBr==zAccept || !fossil_isalnum(zBr[-1]))
1327 ){
1328 return;
1329 }
1330 }
1331 nQP = cgi_qp_count();
1332 if( nQP<1 ) return;
1333 isMatch = glob_multi_match(zGlob, g.zPath);
1334 if( !isMatch ) return;
1335
1336 /* Check for exceptions to the restriction on the number of query
1337 ** parameters. */
1338 zGlob = db_get("robot-restrict-qp",0);
1339 if( zGlob && zGlob[0] ){
1340 char *zPath = mprintf("%s/%d", g.zPath, nQP);
1341 isMatch = glob_multi_match(zGlob, zPath);
1342 fossil_free(zPath);
1343 if( isMatch ) return;
1344 }
1345
1346 /* If we reach this point, it means we have a situation where we
1347 ** want to restrict the activity of a robot.
1348 */
1349 g.isHuman = 0;
1350 (void)exclude_spiders(0);
1351 cgi_reply();
1352 fossil_exit(0);
1353 }
1354
1355 /*
1356 ** When this routine is called, we know that the request does not
1357 ** have a login on the present repository. This routine checks to
1358 ** see if their login cookie might be for another member of the
1359 ** login-group.
@@ -1388,11 +1369,11 @@
1388 **
1389 ** g.userUid Database USER.UID value. Might be -1 for "nobody"
1390 ** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
1391 ** g.perm Permissions granted to this user
1392 ** g.anon Permissions that would be available to anonymous
1393 ** g.isHuman True if the user is human, not a spider or robot
1394 ** g.perm Populated based on user account's capabilities
1395 **
1396 */
1397 void login_check_credentials(void){
1398 int uid = 0; /* User id */
@@ -1429,11 +1410,11 @@
1429 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
1430 }
1431 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1432 zCap = "sxy";
1433 g.noPswd = 1;
1434 g.isHuman = 1;
1435 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1436 " FROM user WHERE uid=%d", uid);
1437 login_create_csrf_secret(zSeed);
1438 fossil_free(zSeed);
1439 }
@@ -1457,33 +1438,38 @@
1457 }
1458 }
1459 }
1460 if( zUser==0 ){
1461 /* Invalid cookie */
1462 }else if( fossil_strcmp(zUser, "anonymous")==0 ){
1463 /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
1464 ** too old and the sha1 hash of TIME/SECRET must match HASH.
1465 ** SECRET is the "captcha-secret" value in the repository.
 
 
 
 
1466 */
1467 double rTime = atof(zArg);
 
1468 Blob b;
1469 char *zSecret;
1470 int n = 0;
1471
1472 do{
1473 blob_zero(&b);
1474 zSecret = captcha_secret(n++);
1475 if( zSecret==0 ) break;
1476 blob_appendf(&b, "%s/%s", zArg, zSecret);
1477 sha1sum_blob(&b, &b);
1478 if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1479 uid = db_int(0,
1480 "SELECT uid FROM user WHERE login='anonymous'"
1481 " AND octet_length(cap)>0"
1482 " AND octet_length(pw)>0"
1483 " AND %.17g+0.25>julianday('now')",
1484 rTime
1485 );
1486 }
1487 }while( uid==0 );
1488 blob_reset(&b);
1489 }else{
@@ -1559,12 +1545,15 @@
1559 login_create_csrf_secret("none");
1560 }
1561
1562 login_set_uid(uid, zCap);
1563
1564 /* Maybe restrict access to robots */
1565 login_restrict_robot_access();
 
 
 
1566 }
1567
1568 /*
1569 ** Set the current logged in user to be uid. zCap is precomputed
1570 ** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1599,15 +1588,15 @@
1599 g.userUid = uid;
1600 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
1601 g.zLogin = 0;
1602 }
1603 if( PB("isrobot") ){
1604 g.isHuman = 0;
1605 }else if( g.zLogin==0 ){
1606 g.isHuman = isHuman(P("HTTP_USER_AGENT"));
1607 }else{
1608 g.isHuman = 1;
1609 }
1610
1611 /* Set the capabilities */
1612 login_replace_capabilities(zCap, 0);
1613
@@ -1617,11 +1606,11 @@
1617 ** enabled for this repository and make appropriate adjustments to the
1618 ** permission flags if it is. This should be done before the permissions
1619 ** are (potentially) copied to the anonymous permission set; otherwise,
1620 ** those will be out-of-sync.
1621 */
1622 if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
1623 int autoLink = db_get_int("auto-hyperlink",1);
1624 if( autoLink==1 ){
1625 g.jsHref = 1;
1626 g.perm.Hyperlink = 1;
1627 }else if( autoLink==2 ){
@@ -1927,11 +1916,11 @@
1927 blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
1928 }
1929 if( zQS && zQS[0] ){
1930 blob_appendf(&redir, "%%3f%T", zQS);
1931 }
1932 if( anonOk ) blob_append(&redir, "&anon", 5);
1933 cgi_redirect(blob_str(&redir));
1934 /* NOTREACHED */
1935 assert(0);
1936 }
1937 }
@@ -1941,11 +1930,11 @@
1941 ** the anonymous user has Hyperlink permission, then paint a mesage
1942 ** to inform the user that much more information is available by
1943 ** logging in as anonymous.
1944 */
1945 void login_anonymous_available(void){
1946 if( !g.perm.Hyperlink && g.anon.Hyperlink ){
1947 const char *zUrl = PD("PATH_INFO", "");
1948 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
1949 @ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
1950 @ to enable hyperlinks.</p>
1951 }
1952
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160
161 if( zUsername==0 ) return 0;
162 else if( zPassword==0 ) return 0;
163 else if( zCS==0 ) return 0;
164 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
165 else if( anon_cookie_lifespan()==0 ) return 0;
166 while( 1/*exit-by-break*/ ){
167 zPw = captcha_decode((unsigned int)atoi(zCS), n);
168 if( zPw==0 ) return 0;
169 if( fossil_stricmp(zPw, zPassword)==0 ) break;
170 n++;
@@ -338,33 +339,62 @@
339 *zDest = zCookie;
340 }else{
341 free(zCookie);
342 }
343 }
344
345 /*
346 ** SETTING: anon-cookie-lifespan width=10 default=480
347 ** The number of minutes for which an anonymous login cookie is
348 ** valid. Anonymous logins are prohibited if this value is zero.
349 */
350
351
352 /*
353 ** The default lifetime of an anoymous cookie, in minutes.
354 */
355 #define ANONYMOUS_COOKIE_LIFESPAN (8*60)
356
357 /*
358 ** Return the lifetime of an anonymous cookie, in minutes.
359 */
360 int anon_cookie_lifespan(void){
361 static int lifespan = -1;
362 if( lifespan<0 ){
363 lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
364 if( lifespan<0 ) lifespan = 0;
365 }
366 return lifespan;
367 }
368
369 /* Sets a cookie for an anonymous user login, which looks like this:
370 **
371 ** HASH/TIME/anonymous
372 **
373 ** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET
374 ** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value.
375 **
376 ** If zCookieDest is not NULL then the generated cookie is assigned to
377 ** *zCookieDest and the caller must eventually free() it.
378 **
379 ** If bSessionCookie is true, the cookie will be a session cookie.
380 **
381 ** Search for tag-20250817a to find the code that recognizes this cookie.
382 */
383 void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
384 char *zNow; /* Current time (julian day number) */
385 char *zCookie; /* The login cookie */
386 const char *zUserAgent; /* The user agent */
387 const char *zCookieName; /* Name of the login cookie */
388 Blob b; /* Blob used during cookie construction */
389 int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
390 zCookieName = login_cookie_name();
391 zNow = db_text("0", "SELECT julianday('now')");
392 assert( zCookieName && zNow );
393 blob_init(&b, zNow, -1);
394 zUserAgent = PD("HTTP_USER_AGENT","nil");
395 blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0));
396 sha1sum_blob(&b, &b);
397 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
398 blob_reset(&b);
399 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
400 if( zCookieDest ){
@@ -581,17 +611,35 @@
611 /* If the "Reset Password" button in the form was pressed, render
612 ** the Request Password Reset page in place of this one. */
613 login_reqpwreset_page();
614 return;
615 }
616
617 /* If the "anon" query parameter is 1 or 2, that means rework the web-page
618 ** to make it a more user-friendly captcha. Extraneous text and boxes
619 ** are omitted. The user has just the captcha image and an entry box
620 ** and a "Verify" button. Underneath is the same login page for user
621 ** "anonymous", just displayed in an easier to digest format for one-time
622 ** visitors.
623 **
624 ** anon=1 is advisory and only has effect if there is not some other login
625 ** cookie. anon=2 means always show the captcha.
626 */
627 anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
628 if( anonFlag==2 ){
629 g.zLogin = 0;
630 }else{
631 login_check_credentials();
632 if( g.zLogin!=0 ) anonFlag = 0;
633 }
634
635 fossil_redirect_to_https_if_needed(1);
636 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
637 constant_time_cmp_function, 0, 0);
638 zUsername = P("u");
639 zPasswd = P("p");
640
641 /* Handle log-out requests */
642 if( P("out") && cgi_csrf_safe(2) ){
643 login_clear_login_data();
644 login_redirect_to_g();
645 return;
@@ -717,10 +765,11 @@
765 login_redirect_to_g();
766 }
767 }
768 style_set_current_feature("login");
769 style_header("Login/Logout");
770 if( anonFlag==2 ) g.zLogin = 0;
771 style_adunit_config(ADUNIT_OFF);
772 @ %s(zErrMsg)
773 if( zGoto && !noAnon ){
774 char *zAbbrev = fossil_strdup(zGoto);
775 int i;
@@ -728,12 +777,12 @@
777 zAbbrev[i] = 0;
778 if( g.zLogin ){
779 @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
780 @ to access <b>%h(zAbbrev)</b>.
781 }else if( anonFlag ){
782 @ <p><b>Verify that you are human by typing in the 8-character text
783 @ password shown below.</b></p>
784 }else{
785 @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
786 }
787 fossil_free(zAbbrev);
788 }
@@ -748,26 +797,27 @@
797 if( zGoto ){
798 @ <input type="hidden" name="g" value="%h(zGoto)">
799 }
800 if( anonFlag ){
801 @ <input type="hidden" name="anon" value="1">
802 @ <input type="hidden" name="u" value="anonymous">
803 }
804 if( g.zLogin ){
805 @ <p>Currently logged in as <b>%h(g.zLogin)</b>.
806 @ <input type="submit" name="out" value="Logout" autofocus></p>
807 @ </form>
808 }else{
809 unsigned int uSeed = captcha_seed();
810 if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
811 zAnonPw = db_text(0, "SELECT pw FROM user"
812 " WHERE login='anonymous'"
813 " AND cap!=''");
814 }else{
815 zAnonPw = 0;
816 }
817 @ <table class="login_out">
818 if( P("HTTPS")==0 && !anonFlag ){
819 @ <tr><td class="form_label">Warning:</td>
820 @ <td><span class='securityWarning'>
821 @ Login information, including the password,
822 @ will be sent in the clear over an unencrypted connection.
823 if( !g.sslNotAvailable ){
@@ -774,41 +824,55 @@
824 @ Consider logging in at
825 @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
826 }
827 @ </span></td></tr>
828 }
829 if( !anonFlag ){
830 @ <tr>
831 @ <td class="form_label" id="userlabel1">User ID:</td>
832 @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
833 @ size="30" value="" autofocus></td>
834 @ </tr>
835 }
836 @ <tr>
837 @ <td class="form_label" id="pswdlabel">Password:</td>
838 @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
839 @ name="p" value="" size="30"%s(anonFlag ? " autofocus" : "")>
840 if( anonFlag ){
841 @ </td></tr>
842 @ <tr>
843 @ <td></td><td>\
844 captcha_speakit_button(uSeed, "Read the password out loud");
845 }else if( zAnonPw && !noAnon ){
846 captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
847 }
848 @ </td>
849 @ </tr>
850 if( !anonFlag ){
851 @ <tr>
852 @ <td></td>
853 @ <td><input type="checkbox" name="remember" value="1" \
854 @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
855 @ <label for="remember-me">Remember me?</label></td>
856 @ </tr>
857 @ <tr>
858 @ <td></td>
859 @ <td><input type="submit" name="in" value="Login">
860 @ </tr>
861 }else{
862 @ <tr>
863 @ <td></td>
864 @ <td><input type="submit" name="in" value="Verify that I am human">
865 @ </tr>
866 }
867 if( !anonFlag && !noAnon && login_self_register_available(0) ){
868 @ <tr>
869 @ <td></td>
870 @ <td><input type="submit" name="self" value="Create A New Account">
871 @ </tr>
872 }
873 if( !anonFlag && login_self_password_reset_available() ){
874 @ <tr>
875 @ <td></td>
876 @ <td><input type="submit" name="pwreset" value="Reset My Password">
877 @ </tr>
878 }
@@ -817,27 +881,29 @@
881 const char *zDecoded = captcha_decode(uSeed, 0);
882 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
883 char *zCaptcha = captcha_render(zDecoded);
884
885 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
886 if( !anonFlag ){
887 @ Visitors may enter <b>anonymous</b> as the user-ID with
888 @ the 8-character hexadecimal password shown below:</p>
889 }
890 @ <div class="captcha"><table class="captcha"><tr><td>\
891 @ <pre class="captcha">
892 @ %h(zCaptcha)
893 @ </pre></td></tr></table>
894 if( bAutoCaptcha && !anonFlag ) {
895 @ <input type="button" value="Fill out captcha" id='autofillButton' \
896 @ data-af='%s(zDecoded)'>
897 builtin_request_js("login.js");
898 }
899 @ </div>
900 free(zCaptcha);
901 }
902 @ </form>
903 }
904 if( login_is_individual() && !anonFlag ){
905 if( g.perm.EmailAlert && alert_enabled() ){
906 @ <hr>
907 @ <p>Configure <a href="%R/alerts">Email Alerts</a>
908 @ for user <b>%h(g.zLogin)</b></p>
909 }
@@ -845,15 +911,18 @@
911 @ <hr><p>
912 @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
913 @ post timeline</a> for user <b>%h(g.zLogin)</b></p>
914 }
915 }
916 if( !anonFlag ){
917 @ <hr><p>
918 @ Select your preferred <a href="%R/skins">site skin</a>.
919 @ </p>
920 @ <hr><p>
921 @ Manage your <a href="%R/cookies">cookies</a> or your
922 @ <a href="%R/tokens">access tokens</a>.</p>
923 }
924 if( login_is_individual() ){
925 if( g.perm.Password ){
926 char *zRPW = fossil_random_password(12);
927 @ <hr>
928 @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
@@ -1262,98 +1331,10 @@
1331 }
1332 fossil_free(zDecode);
1333 return uid;
1334 }
1335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1336 /*
1337 ** When this routine is called, we know that the request does not
1338 ** have a login on the present repository. This routine checks to
1339 ** see if their login cookie might be for another member of the
1340 ** login-group.
@@ -1388,11 +1369,11 @@
1369 **
1370 ** g.userUid Database USER.UID value. Might be -1 for "nobody"
1371 ** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
1372 ** g.perm Permissions granted to this user
1373 ** g.anon Permissions that would be available to anonymous
1374 ** g.isRobot True if the client is known to be a spider or robot
1375 ** g.perm Populated based on user account's capabilities
1376 **
1377 */
1378 void login_check_credentials(void){
1379 int uid = 0; /* User id */
@@ -1429,11 +1410,11 @@
1410 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
1411 }
1412 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1413 zCap = "sxy";
1414 g.noPswd = 1;
1415 g.isRobot = 0;
1416 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1417 " FROM user WHERE uid=%d", uid);
1418 login_create_csrf_secret(zSeed);
1419 fossil_free(zSeed);
1420 }
@@ -1457,33 +1438,38 @@
1438 }
1439 }
1440 }
1441 if( zUser==0 ){
1442 /* Invalid cookie */
1443 }else if( fossil_strcmp(zUser, "anonymous")==0
1444 && anon_cookie_lifespan()>0 ){
1445 /* Cookies of the form "HASH/TIME/anonymous". The TIME must
1446 ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
1447 ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
1448 ** is the HTTP_USER_AGENT of the client and SECRET is the
1449 ** "captcha-secret" value in the repository. See tag-20250817a
1450 ** for the code the creates this cookie.
1451 */
1452 double rTime = atof(zArg);
1453 const char *zUserAgent = PD("HTTP_USER_AGENT","nil");
1454 Blob b;
1455 char *zSecret;
1456 int n = 0;
1457
1458 do{
1459 blob_zero(&b);
1460 zSecret = captcha_secret(n++);
1461 if( zSecret==0 ) break;
1462 blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret);
1463 sha1sum_blob(&b, &b);
1464 if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1465 uid = db_int(0,
1466 "SELECT uid FROM user WHERE login='anonymous'"
1467 " AND octet_length(cap)>0"
1468 " AND octet_length(pw)>0"
1469 " AND %.17g>julianday('now')",
1470 rTime+anon_cookie_lifespan()/1440.0
1471 );
1472 }
1473 }while( uid==0 );
1474 blob_reset(&b);
1475 }else{
@@ -1559,12 +1545,15 @@
1545 login_create_csrf_secret("none");
1546 }
1547
1548 login_set_uid(uid, zCap);
1549
1550 /* Maybe restrict access by robots */
1551 if( g.zLogin==0 && robot_restrict(g.zPath) ){
1552 cgi_reply();
1553 fossil_exit(0);
1554 }
1555 }
1556
1557 /*
1558 ** Set the current logged in user to be uid. zCap is precomputed
1559 ** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1599,15 +1588,15 @@
1588 g.userUid = uid;
1589 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
1590 g.zLogin = 0;
1591 }
1592 if( PB("isrobot") ){
1593 g.isRobot = 1;
1594 }else if( g.zLogin==0 ){
1595 g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
1596 }else{
1597 g.isRobot = 0;
1598 }
1599
1600 /* Set the capabilities */
1601 login_replace_capabilities(zCap, 0);
1602
@@ -1617,11 +1606,11 @@
1606 ** enabled for this repository and make appropriate adjustments to the
1607 ** permission flags if it is. This should be done before the permissions
1608 ** are (potentially) copied to the anonymous permission set; otherwise,
1609 ** those will be out-of-sync.
1610 */
1611 if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
1612 int autoLink = db_get_int("auto-hyperlink",1);
1613 if( autoLink==1 ){
1614 g.jsHref = 1;
1615 g.perm.Hyperlink = 1;
1616 }else if( autoLink==2 ){
@@ -1927,11 +1916,11 @@
1916 blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
1917 }
1918 if( zQS && zQS[0] ){
1919 blob_appendf(&redir, "%%3f%T", zQS);
1920 }
1921 if( anonOk ) blob_append(&redir, "&anon=1", 7);
1922 cgi_redirect(blob_str(&redir));
1923 /* NOTREACHED */
1924 assert(0);
1925 }
1926 }
@@ -1941,11 +1930,11 @@
1930 ** the anonymous user has Hyperlink permission, then paint a mesage
1931 ** to inform the user that much more information is available by
1932 ** logging in as anonymous.
1933 */
1934 void login_anonymous_available(void){
1935 if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
1936 const char *zUrl = PD("PATH_INFO", "");
1937 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
1938 @ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
1939 @ to enable hyperlinks.</p>
1940 }
1941
+7 -2
--- src/main.c
+++ src/main.c
@@ -233,11 +233,12 @@
233233
* applicable when using SEE on Windows or Linux. */
234234
#endif
235235
int useLocalauth; /* No login required if from 127.0.0.1 */
236236
int noPswd; /* Logged in without password (on 127.0.0.1) */
237237
int userUid; /* Integer user id */
238
- int isHuman; /* True if access by a human, not a spider or bot */
238
+ int isRobot; /* True if the client is definitely a robot. False
239
+ ** negatives are common for this flag */
239240
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
240241
** accessed through get_comment_format(). */
241242
const char *zSockName; /* Name of the unix-domain socket file */
242243
const char *zSockMode; /* File permissions for unix-domain socket */
243244
const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
@@ -2439,11 +2440,11 @@
24392440
** can be multiple "redirect:" lines that are
24402441
** processed in order. If the REPO is "*", then
24412442
** an unconditional redirect to URL is taken.
24422443
** When "*" is used a 301 permanent redirect is
24432444
** issued and the tail and query string from the
2444
-** original query are appeneded onto URL.
2445
+** original query are appended onto URL.
24452446
**
24462447
** jsmode: VALUE Specifies the delivery mode for JavaScript
24472448
** files. See the help text for the --jsmode
24482449
** flag of the http command.
24492450
**
@@ -3100,10 +3101,11 @@
31003101
** breaking legacy.
31013102
**
31023103
** Options:
31033104
** --csrf-safe N Set cgi_csrf_safe() to to return N
31043105
** --nobody Pretend to be user "nobody"
3106
+** --ssh-sim Pretend to be over an SSH connection
31053107
** --test Do not do special "sync" processing when operating
31063108
** over an SSH link
31073109
** --th-trace Trace TH1 execution (for debugging purposes)
31083110
** --usercap CAP User capability string (Default: "sxy")
31093111
*/
@@ -3111,10 +3113,13 @@
31113113
const char *zIpAddr; /* IP address of remote client */
31123114
const char *zUserCap;
31133115
int bTest = 0;
31143116
const char *zCsrfSafe = find_option("csrf-safe",0,1);
31153117
3118
+ if( find_option("ssh-sim",0,0)!=0 ){
3119
+ putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
3120
+ }
31163121
Th_InitTraceLog();
31173122
if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
31183123
zUserCap = find_option("usercap",0,1);
31193124
if( !find_option("nobody",0,0) ){
31203125
if( zUserCap==0 ){
31213126
--- src/main.c
+++ src/main.c
@@ -233,11 +233,12 @@
233 * applicable when using SEE on Windows or Linux. */
234 #endif
235 int useLocalauth; /* No login required if from 127.0.0.1 */
236 int noPswd; /* Logged in without password (on 127.0.0.1) */
237 int userUid; /* Integer user id */
238 int isHuman; /* True if access by a human, not a spider or bot */
 
239 int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
240 ** accessed through get_comment_format(). */
241 const char *zSockName; /* Name of the unix-domain socket file */
242 const char *zSockMode; /* File permissions for unix-domain socket */
243 const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
@@ -2439,11 +2440,11 @@
2439 ** can be multiple "redirect:" lines that are
2440 ** processed in order. If the REPO is "*", then
2441 ** an unconditional redirect to URL is taken.
2442 ** When "*" is used a 301 permanent redirect is
2443 ** issued and the tail and query string from the
2444 ** original query are appeneded onto URL.
2445 **
2446 ** jsmode: VALUE Specifies the delivery mode for JavaScript
2447 ** files. See the help text for the --jsmode
2448 ** flag of the http command.
2449 **
@@ -3100,10 +3101,11 @@
3100 ** breaking legacy.
3101 **
3102 ** Options:
3103 ** --csrf-safe N Set cgi_csrf_safe() to to return N
3104 ** --nobody Pretend to be user "nobody"
 
3105 ** --test Do not do special "sync" processing when operating
3106 ** over an SSH link
3107 ** --th-trace Trace TH1 execution (for debugging purposes)
3108 ** --usercap CAP User capability string (Default: "sxy")
3109 */
@@ -3111,10 +3113,13 @@
3111 const char *zIpAddr; /* IP address of remote client */
3112 const char *zUserCap;
3113 int bTest = 0;
3114 const char *zCsrfSafe = find_option("csrf-safe",0,1);
3115
 
 
 
3116 Th_InitTraceLog();
3117 if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
3118 zUserCap = find_option("usercap",0,1);
3119 if( !find_option("nobody",0,0) ){
3120 if( zUserCap==0 ){
3121
--- src/main.c
+++ src/main.c
@@ -233,11 +233,12 @@
233 * applicable when using SEE on Windows or Linux. */
234 #endif
235 int useLocalauth; /* No login required if from 127.0.0.1 */
236 int noPswd; /* Logged in without password (on 127.0.0.1) */
237 int userUid; /* Integer user id */
238 int isRobot; /* True if the client is definitely a robot. False
239 ** negatives are common for this flag */
240 int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
241 ** accessed through get_comment_format(). */
242 const char *zSockName; /* Name of the unix-domain socket file */
243 const char *zSockMode; /* File permissions for unix-domain socket */
244 const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
@@ -2439,11 +2440,11 @@
2440 ** can be multiple "redirect:" lines that are
2441 ** processed in order. If the REPO is "*", then
2442 ** an unconditional redirect to URL is taken.
2443 ** When "*" is used a 301 permanent redirect is
2444 ** issued and the tail and query string from the
2445 ** original query are appended onto URL.
2446 **
2447 ** jsmode: VALUE Specifies the delivery mode for JavaScript
2448 ** files. See the help text for the --jsmode
2449 ** flag of the http command.
2450 **
@@ -3100,10 +3101,11 @@
3101 ** breaking legacy.
3102 **
3103 ** Options:
3104 ** --csrf-safe N Set cgi_csrf_safe() to to return N
3105 ** --nobody Pretend to be user "nobody"
3106 ** --ssh-sim Pretend to be over an SSH connection
3107 ** --test Do not do special "sync" processing when operating
3108 ** over an SSH link
3109 ** --th-trace Trace TH1 execution (for debugging purposes)
3110 ** --usercap CAP User capability string (Default: "sxy")
3111 */
@@ -3111,10 +3113,13 @@
3113 const char *zIpAddr; /* IP address of remote client */
3114 const char *zUserCap;
3115 int bTest = 0;
3116 const char *zCsrfSafe = find_option("csrf-safe",0,1);
3117
3118 if( find_option("ssh-sim",0,0)!=0 ){
3119 putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
3120 }
3121 Th_InitTraceLog();
3122 if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
3123 zUserCap = find_option("usercap",0,1);
3124 if( !find_option("nobody",0,0) ){
3125 if( zUserCap==0 ){
3126
+13
--- src/main.mk
+++ src/main.mk
@@ -119,10 +119,11 @@
119119
$(SRCDIR)/purge.c \
120120
$(SRCDIR)/rebuild.c \
121121
$(SRCDIR)/regexp.c \
122122
$(SRCDIR)/repolist.c \
123123
$(SRCDIR)/report.c \
124
+ $(SRCDIR)/robot.c \
124125
$(SRCDIR)/rss.c \
125126
$(SRCDIR)/schema.c \
126127
$(SRCDIR)/search.c \
127128
$(SRCDIR)/security_audit.c \
128129
$(SRCDIR)/setup.c \
@@ -236,10 +237,11 @@
236237
$(SRCDIR)/fossil.page.chat.js \
237238
$(SRCDIR)/fossil.page.fileedit.js \
238239
$(SRCDIR)/fossil.page.forumpost.js \
239240
$(SRCDIR)/fossil.page.pikchrshow.js \
240241
$(SRCDIR)/fossil.page.pikchrshowasm.js \
242
+ $(SRCDIR)/fossil.page.ticket.js \
241243
$(SRCDIR)/fossil.page.whistory.js \
242244
$(SRCDIR)/fossil.page.wikiedit.js \
243245
$(SRCDIR)/fossil.pikchr.js \
244246
$(SRCDIR)/fossil.popupwidget.js \
245247
$(SRCDIR)/fossil.storage.js \
@@ -385,10 +387,11 @@
385387
$(OBJDIR)/purge_.c \
386388
$(OBJDIR)/rebuild_.c \
387389
$(OBJDIR)/regexp_.c \
388390
$(OBJDIR)/repolist_.c \
389391
$(OBJDIR)/report_.c \
392
+ $(OBJDIR)/robot_.c \
390393
$(OBJDIR)/rss_.c \
391394
$(OBJDIR)/schema_.c \
392395
$(OBJDIR)/search_.c \
393396
$(OBJDIR)/security_audit_.c \
394397
$(OBJDIR)/setup_.c \
@@ -535,10 +538,11 @@
535538
$(OBJDIR)/purge.o \
536539
$(OBJDIR)/rebuild.o \
537540
$(OBJDIR)/regexp.o \
538541
$(OBJDIR)/repolist.o \
539542
$(OBJDIR)/report.o \
543
+ $(OBJDIR)/robot.o \
540544
$(OBJDIR)/rss.o \
541545
$(OBJDIR)/schema.o \
542546
$(OBJDIR)/search.o \
543547
$(OBJDIR)/security_audit.o \
544548
$(OBJDIR)/setup.o \
@@ -878,10 +882,11 @@
878882
$(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
879883
$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
880884
$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
881885
$(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
882886
$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
887
+ $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
883888
$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
884889
$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
885890
$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
886891
$(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
887892
$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -1768,10 +1773,18 @@
17681773
17691774
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
17701775
$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
17711776
17721777
$(OBJDIR)/report.h: $(OBJDIR)/headers
1778
+
1779
+$(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(OBJDIR)/translate
1780
+ $(OBJDIR)/translate $(SRCDIR)/robot.c >$@
1781
+
1782
+$(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
1783
+ $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
1784
+
1785
+$(OBJDIR)/robot.h: $(OBJDIR)/headers
17731786
17741787
$(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate
17751788
$(OBJDIR)/translate $(SRCDIR)/rss.c >$@
17761789
17771790
$(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
17781791
--- src/main.mk
+++ src/main.mk
@@ -119,10 +119,11 @@
119 $(SRCDIR)/purge.c \
120 $(SRCDIR)/rebuild.c \
121 $(SRCDIR)/regexp.c \
122 $(SRCDIR)/repolist.c \
123 $(SRCDIR)/report.c \
 
124 $(SRCDIR)/rss.c \
125 $(SRCDIR)/schema.c \
126 $(SRCDIR)/search.c \
127 $(SRCDIR)/security_audit.c \
128 $(SRCDIR)/setup.c \
@@ -236,10 +237,11 @@
236 $(SRCDIR)/fossil.page.chat.js \
237 $(SRCDIR)/fossil.page.fileedit.js \
238 $(SRCDIR)/fossil.page.forumpost.js \
239 $(SRCDIR)/fossil.page.pikchrshow.js \
240 $(SRCDIR)/fossil.page.pikchrshowasm.js \
 
241 $(SRCDIR)/fossil.page.whistory.js \
242 $(SRCDIR)/fossil.page.wikiedit.js \
243 $(SRCDIR)/fossil.pikchr.js \
244 $(SRCDIR)/fossil.popupwidget.js \
245 $(SRCDIR)/fossil.storage.js \
@@ -385,10 +387,11 @@
385 $(OBJDIR)/purge_.c \
386 $(OBJDIR)/rebuild_.c \
387 $(OBJDIR)/regexp_.c \
388 $(OBJDIR)/repolist_.c \
389 $(OBJDIR)/report_.c \
 
390 $(OBJDIR)/rss_.c \
391 $(OBJDIR)/schema_.c \
392 $(OBJDIR)/search_.c \
393 $(OBJDIR)/security_audit_.c \
394 $(OBJDIR)/setup_.c \
@@ -535,10 +538,11 @@
535 $(OBJDIR)/purge.o \
536 $(OBJDIR)/rebuild.o \
537 $(OBJDIR)/regexp.o \
538 $(OBJDIR)/repolist.o \
539 $(OBJDIR)/report.o \
 
540 $(OBJDIR)/rss.o \
541 $(OBJDIR)/schema.o \
542 $(OBJDIR)/search.o \
543 $(OBJDIR)/security_audit.o \
544 $(OBJDIR)/setup.o \
@@ -878,10 +882,11 @@
878 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
879 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
880 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
881 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
882 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
 
883 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
884 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
885 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
886 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
887 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -1768,10 +1773,18 @@
1768
1769 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
1770 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
1771
1772 $(OBJDIR)/report.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1773
1774 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate
1775 $(OBJDIR)/translate $(SRCDIR)/rss.c >$@
1776
1777 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
1778
--- src/main.mk
+++ src/main.mk
@@ -119,10 +119,11 @@
119 $(SRCDIR)/purge.c \
120 $(SRCDIR)/rebuild.c \
121 $(SRCDIR)/regexp.c \
122 $(SRCDIR)/repolist.c \
123 $(SRCDIR)/report.c \
124 $(SRCDIR)/robot.c \
125 $(SRCDIR)/rss.c \
126 $(SRCDIR)/schema.c \
127 $(SRCDIR)/search.c \
128 $(SRCDIR)/security_audit.c \
129 $(SRCDIR)/setup.c \
@@ -236,10 +237,11 @@
237 $(SRCDIR)/fossil.page.chat.js \
238 $(SRCDIR)/fossil.page.fileedit.js \
239 $(SRCDIR)/fossil.page.forumpost.js \
240 $(SRCDIR)/fossil.page.pikchrshow.js \
241 $(SRCDIR)/fossil.page.pikchrshowasm.js \
242 $(SRCDIR)/fossil.page.ticket.js \
243 $(SRCDIR)/fossil.page.whistory.js \
244 $(SRCDIR)/fossil.page.wikiedit.js \
245 $(SRCDIR)/fossil.pikchr.js \
246 $(SRCDIR)/fossil.popupwidget.js \
247 $(SRCDIR)/fossil.storage.js \
@@ -385,10 +387,11 @@
387 $(OBJDIR)/purge_.c \
388 $(OBJDIR)/rebuild_.c \
389 $(OBJDIR)/regexp_.c \
390 $(OBJDIR)/repolist_.c \
391 $(OBJDIR)/report_.c \
392 $(OBJDIR)/robot_.c \
393 $(OBJDIR)/rss_.c \
394 $(OBJDIR)/schema_.c \
395 $(OBJDIR)/search_.c \
396 $(OBJDIR)/security_audit_.c \
397 $(OBJDIR)/setup_.c \
@@ -535,10 +538,11 @@
538 $(OBJDIR)/purge.o \
539 $(OBJDIR)/rebuild.o \
540 $(OBJDIR)/regexp.o \
541 $(OBJDIR)/repolist.o \
542 $(OBJDIR)/report.o \
543 $(OBJDIR)/robot.o \
544 $(OBJDIR)/rss.o \
545 $(OBJDIR)/schema.o \
546 $(OBJDIR)/search.o \
547 $(OBJDIR)/security_audit.o \
548 $(OBJDIR)/setup.o \
@@ -878,10 +882,11 @@
882 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
883 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
884 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
885 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
886 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
887 $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
888 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
889 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
890 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
891 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
892 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -1768,10 +1773,18 @@
1773
1774 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
1775 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
1776
1777 $(OBJDIR)/report.h: $(OBJDIR)/headers
1778
1779 $(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(OBJDIR)/translate
1780 $(OBJDIR)/translate $(SRCDIR)/robot.c >$@
1781
1782 $(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
1783 $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
1784
1785 $(OBJDIR)/robot.h: $(OBJDIR)/headers
1786
1787 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate
1788 $(OBJDIR)/translate $(SRCDIR)/rss.c >$@
1789
1790 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
1791
+1 -1
--- src/manifest.c
+++ src/manifest.c
@@ -2734,11 +2734,11 @@
27342734
}
27352735
zName = p->aTag[i].zName;
27362736
zValue = p->aTag[i].zValue;
27372737
if( strcmp(zName, "*branch")==0 ){
27382738
blob_appendf(&comment,
2739
- " Move to branch [/timeline?r=%h&nd&dp=%!S&unhide | %h].",
2739
+ " Move to branch [/timeline?r=%t&nd&dp=%!S&unhide | %h].",
27402740
zValue, zTagUuid, zValue);
27412741
branchMove = 1;
27422742
continue;
27432743
}else if( strcmp(zName, "*bgcolor")==0 ){
27442744
blob_appendf(&comment,
27452745
--- src/manifest.c
+++ src/manifest.c
@@ -2734,11 +2734,11 @@
2734 }
2735 zName = p->aTag[i].zName;
2736 zValue = p->aTag[i].zValue;
2737 if( strcmp(zName, "*branch")==0 ){
2738 blob_appendf(&comment,
2739 " Move to branch [/timeline?r=%h&nd&dp=%!S&unhide | %h].",
2740 zValue, zTagUuid, zValue);
2741 branchMove = 1;
2742 continue;
2743 }else if( strcmp(zName, "*bgcolor")==0 ){
2744 blob_appendf(&comment,
2745
--- src/manifest.c
+++ src/manifest.c
@@ -2734,11 +2734,11 @@
2734 }
2735 zName = p->aTag[i].zName;
2736 zValue = p->aTag[i].zValue;
2737 if( strcmp(zName, "*branch")==0 ){
2738 blob_appendf(&comment,
2739 " Move to branch [/timeline?r=%t&nd&dp=%!S&unhide | %h].",
2740 zValue, zTagUuid, zValue);
2741 branchMove = 1;
2742 continue;
2743 }else if( strcmp(zName, "*bgcolor")==0 ){
2744 blob_appendf(&comment,
2745
+1 -1
--- src/markdown.md
+++ src/markdown.md
@@ -188,11 +188,11 @@
188188
189189
## Miscellaneous ##
190190
191191
> * In-line images are made using **\!\[alt-text\]\(image-URL\)**.
192192
> * Use HTML for advanced formatting such as forms, noting that certain
193
-> tags are [disallowed in some contexts](/help?cmd=safe-html).
193
+> tags are [disallowed in some contexts](/help/safe-html).
194194
> * **\<!--** HTML-style comments **-->** are supported.
195195
> * Escape special characters (ex: **\[** **\(** **\|** **\***)
196196
> using backslash (ex: **\\\[** **\\\(** **\\\|** **\\\***).
197197
> * A line consisting of **---**, **\*\*\***, or **\_\_\_** is a horizontal
198198
> rule. Spaces and extra **-**/**\***/**_** are allowed.
199199
--- src/markdown.md
+++ src/markdown.md
@@ -188,11 +188,11 @@
188
189 ## Miscellaneous ##
190
191 > * In-line images are made using **\!\[alt-text\]\(image-URL\)**.
192 > * Use HTML for advanced formatting such as forms, noting that certain
193 > tags are [disallowed in some contexts](/help?cmd=safe-html).
194 > * **\<!--** HTML-style comments **-->** are supported.
195 > * Escape special characters (ex: **\[** **\(** **\|** **\***)
196 > using backslash (ex: **\\\[** **\\\(** **\\\|** **\\\***).
197 > * A line consisting of **---**, **\*\*\***, or **\_\_\_** is a horizontal
198 > rule. Spaces and extra **-**/**\***/**_** are allowed.
199
--- src/markdown.md
+++ src/markdown.md
@@ -188,11 +188,11 @@
188
189 ## Miscellaneous ##
190
191 > * In-line images are made using **\!\[alt-text\]\(image-URL\)**.
192 > * Use HTML for advanced formatting such as forms, noting that certain
193 > tags are [disallowed in some contexts](/help/safe-html).
194 > * **\<!--** HTML-style comments **-->** are supported.
195 > * Escape special characters (ex: **\[** **\(** **\|** **\***)
196 > using backslash (ex: **\\\[** **\\\(** **\\\|** **\\\***).
197 > * A line consisting of **---**, **\*\*\***, or **\_\_\_** is a horizontal
198 > rule. Spaces and extra **-**/**\***/**_** are allowed.
199
+2 -2
--- src/match.c
+++ src/match.c
@@ -142,11 +142,11 @@
142142
if( zPat[0] ) zPat++;
143143
144144
/* Check for regular expression syntax errors. */
145145
if( style==MS_REGEXP ){
146146
ReCompiled *regexp;
147
- const char *zFail = re_compile(&regexp, zOne, 0);
147
+ const char *zFail = fossil_re_compile(&regexp, zOne, 0);
148148
if( zFail ){
149149
re_free(regexp);
150150
continue;
151151
}
152152
p->nPattern++;
@@ -375,11 +375,11 @@
375375
376376
/* Check for regular expression syntax errors. */
377377
if( matchStyle==MS_REGEXP ){
378378
ReCompiled *regexp;
379379
char *zTagDup = fossil_strndup(zTag, i);
380
- zFail = re_compile(&regexp, zTagDup, 0);
380
+ zFail = fossil_re_compile(&regexp, zTagDup, 0);
381381
re_free(regexp);
382382
fossil_free(zTagDup);
383383
}
384384
385385
/* Process success and error results. */
386386
--- src/match.c
+++ src/match.c
@@ -142,11 +142,11 @@
142 if( zPat[0] ) zPat++;
143
144 /* Check for regular expression syntax errors. */
145 if( style==MS_REGEXP ){
146 ReCompiled *regexp;
147 const char *zFail = re_compile(&regexp, zOne, 0);
148 if( zFail ){
149 re_free(regexp);
150 continue;
151 }
152 p->nPattern++;
@@ -375,11 +375,11 @@
375
376 /* Check for regular expression syntax errors. */
377 if( matchStyle==MS_REGEXP ){
378 ReCompiled *regexp;
379 char *zTagDup = fossil_strndup(zTag, i);
380 zFail = re_compile(&regexp, zTagDup, 0);
381 re_free(regexp);
382 fossil_free(zTagDup);
383 }
384
385 /* Process success and error results. */
386
--- src/match.c
+++ src/match.c
@@ -142,11 +142,11 @@
142 if( zPat[0] ) zPat++;
143
144 /* Check for regular expression syntax errors. */
145 if( style==MS_REGEXP ){
146 ReCompiled *regexp;
147 const char *zFail = fossil_re_compile(&regexp, zOne, 0);
148 if( zFail ){
149 re_free(regexp);
150 continue;
151 }
152 p->nPattern++;
@@ -375,11 +375,11 @@
375
376 /* Check for regular expression syntax errors. */
377 if( matchStyle==MS_REGEXP ){
378 ReCompiled *regexp;
379 char *zTagDup = fossil_strndup(zTag, i);
380 zFail = fossil_re_compile(&regexp, zTagDup, 0);
381 re_free(regexp);
382 fossil_free(zTagDup);
383 }
384
385 /* Process success and error results. */
386
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -462,12 +462,12 @@
462462
} CX("</fieldset><!-- .zone-wrapper.input -->");
463463
CX("<fieldset class='zone-wrapper output'>"); {
464464
CX("<legend><div class='button-bar'>");
465465
CX("<button id='btn-render-mode'>Render Mode</button> ");
466466
CX("<span style='white-space:nowrap'>"
467
- "<span id='preview-copy-button' "
468
- "title='Tap to copy to clipboard.'></span>"
467
+ "<button id='preview-copy-button' "
468
+ "title='Tap to copy to clipboard.'><span></span></button>"
469469
"<label for='preview-copy-button' "
470470
"title='Tap to copy to clipboard.'></label>"
471471
"</span>");
472472
CX("</div></legend>");
473473
CX("<div id='pikchr-output-wrapper'>");
474474
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -462,12 +462,12 @@
462 } CX("</fieldset><!-- .zone-wrapper.input -->");
463 CX("<fieldset class='zone-wrapper output'>"); {
464 CX("<legend><div class='button-bar'>");
465 CX("<button id='btn-render-mode'>Render Mode</button> ");
466 CX("<span style='white-space:nowrap'>"
467 "<span id='preview-copy-button' "
468 "title='Tap to copy to clipboard.'></span>"
469 "<label for='preview-copy-button' "
470 "title='Tap to copy to clipboard.'></label>"
471 "</span>");
472 CX("</div></legend>");
473 CX("<div id='pikchr-output-wrapper'>");
474
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -462,12 +462,12 @@
462 } CX("</fieldset><!-- .zone-wrapper.input -->");
463 CX("<fieldset class='zone-wrapper output'>"); {
464 CX("<legend><div class='button-bar'>");
465 CX("<button id='btn-render-mode'>Render Mode</button> ");
466 CX("<span style='white-space:nowrap'>"
467 "<button id='preview-copy-button' "
468 "title='Tap to copy to clipboard.'><span></span></button>"
469 "<label for='preview-copy-button' "
470 "title='Tap to copy to clipboard.'></label>"
471 "</span>");
472 CX("</div></legend>");
473 CX("<div id='pikchr-output-wrapper'>");
474
+1 -1
--- src/printf.c
+++ src/printf.c
@@ -252,11 +252,11 @@
252252
*/
253253
/*
254254
** SETTING: timeline-hard-newlines boolean default=off
255255
**
256256
** If enabled, the timeline honors newline characters in check-in comments.
257
-** In other words, newlines are coverted into <br> for HTML display.
257
+** In other words, newlines are converted into <br> for HTML display.
258258
** The default behavior, when this setting is off, is that newlines are
259259
** treated like any other whitespace character.
260260
*/
261261
262262
/*
263263
--- src/printf.c
+++ src/printf.c
@@ -252,11 +252,11 @@
252 */
253 /*
254 ** SETTING: timeline-hard-newlines boolean default=off
255 **
256 ** If enabled, the timeline honors newline characters in check-in comments.
257 ** In other words, newlines are coverted into <br> for HTML display.
258 ** The default behavior, when this setting is off, is that newlines are
259 ** treated like any other whitespace character.
260 */
261
262 /*
263
--- src/printf.c
+++ src/printf.c
@@ -252,11 +252,11 @@
252 */
253 /*
254 ** SETTING: timeline-hard-newlines boolean default=off
255 **
256 ** If enabled, the timeline honors newline characters in check-in comments.
257 ** In other words, newlines are converted into <br> for HTML display.
258 ** The default behavior, when this setting is off, is that newlines are
259 ** treated like any other whitespace character.
260 */
261
262 /*
263
+243 -92
--- src/regexp.c
+++ src/regexp.c
@@ -53,16 +53,19 @@
5353
** expression and M is the size of the input string. The matcher never
5454
** exhibits exponential behavior. Note that the X{p,q} operator expands
5555
** to p copies of X following by q-p copies of X? and that the size of the
5656
** regular expression in the O(N*M) performance bound is computed after
5757
** this expansion.
58
+**
59
+** To help prevent DoS attacks, the maximum size of the NFA is restricted.
5860
*/
5961
#include "config.h"
6062
#include "regexp.h"
6163
6264
/* The end-of-input character */
6365
#define RE_EOF 0 /* End of input */
66
+#define RE_START 0xfffffff /* Start of input - larger than an UTF-8 */
6467
6568
/* The NFA is implemented as sequence of opcodes taken from the following
6669
** set. Each opcode has a single integer argument.
6770
*/
6871
#define RE_OP_MATCH 1 /* Match the one character in the argument */
@@ -80,10 +83,11 @@
8083
#define RE_OP_DIGIT 13 /* digit: [0-9] */
8184
#define RE_OP_NOTDIGIT 14 /* Not a digit */
8285
#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
8386
#define RE_OP_NOTSPACE 16 /* Not a digit */
8487
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
88
+#define RE_OP_ATSTART 18 /* Currently at the start of the string */
8589
8690
/* Each opcode is a "state" in the NFA */
8791
typedef unsigned short ReStateNumber;
8892
8993
/* Because this is an NFA and not a DFA, multiple states can be active at
@@ -113,13 +117,14 @@
113117
const char *zErr; /* Error message to return */
114118
char *aOp; /* Operators for the virtual machine */
115119
int *aArg; /* Arguments to each operator */
116120
unsigned (*xNextChar)(ReInput*); /* Next character function */
117121
unsigned char zInit[12]; /* Initial text to match */
118
- int nInit; /* Number of characters in zInit */
122
+ int nInit; /* Number of bytes in zInit */
119123
unsigned nState; /* Number of entries in aOp[] and aArg[] */
120124
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
125
+ unsigned mxAlloc; /* Complexity limit */
121126
};
122127
#endif
123128
124129
/* Add a state to the given state set if it is not already there */
125130
static void re_add_state(ReStateSet *pSet, int newState){
@@ -144,11 +149,11 @@
144149
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
145150
&& (p->z[p->i+1]&0xc0)==0x80 ){
146151
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
147152
p->i += 2;
148153
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
149
- }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
154
+ }else if( (c&0xf8)==0xf0 && p->i+2<p->mx && (p->z[p->i]&0xc0)==0x80
150155
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
151156
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
152157
| (p->z[p->i+2]&0x3f);
153158
p->i += 3;
154159
if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
@@ -185,11 +190,11 @@
185190
ReStateSet aStateSet[2], *pThis, *pNext;
186191
ReStateNumber aSpace[100];
187192
ReStateNumber *pToFree;
188193
unsigned int i = 0;
189194
unsigned int iSwap = 0;
190
- int c = RE_EOF+1;
195
+ int c = RE_START;
191196
int cPrev = 0;
192197
int rc = 0;
193198
ReInput in;
194199
195200
in.z = zIn;
@@ -204,10 +209,11 @@
204209
strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
205210
){
206211
in.i++;
207212
}
208213
if( in.i+pRe->nInit>in.mx ) return 0;
214
+ c = RE_START-1;
209215
}
210216
211217
if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
212218
pToFree = 0;
213219
aStateSet[0].aState = aSpace;
@@ -231,10 +237,14 @@
231237
int x = pThis->aState[i];
232238
switch( pRe->aOp[x] ){
233239
case RE_OP_MATCH: {
234240
if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
235241
break;
242
+ }
243
+ case RE_OP_ATSTART: {
244
+ if( cPrev==RE_START ) re_add_state(pThis, x+1);
245
+ break;
236246
}
237247
case RE_OP_ANY: {
238248
if( c!=0 ) re_add_state(pNext, x+1);
239249
break;
240250
}
@@ -284,13 +294,13 @@
284294
rc = 1;
285295
goto re_match_end;
286296
}
287297
case RE_OP_CC_EXC: {
288298
if( c==0 ) break;
289
- /* fall-through */
299
+ /* fall-through */ goto re_op_cc_inc;
290300
}
291
- case RE_OP_CC_INC: {
301
+ case RE_OP_CC_INC: re_op_cc_inc: {
292302
int j = 1;
293303
int n = pRe->aArg[x];
294304
int hit = 0;
295305
for(j=1; j>0 && j<n; j++){
296306
if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
@@ -313,11 +323,13 @@
313323
}
314324
}
315325
}
316326
}
317327
for(i=0; i<pNext->nState; i++){
318
- if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
328
+ int x = pNext->aState[i];
329
+ while( pRe->aOp[x]==RE_OP_GOTO ) x += pRe->aArg[x];
330
+ if( pRe->aOp[x]==RE_OP_ACCEPT ){ rc = 1; break; }
319331
}
320332
re_match_end:
321333
fossil_free(pToFree);
322334
return rc;
323335
}
@@ -325,15 +337,16 @@
325337
/* Resize the opcode and argument arrays for an RE under construction.
326338
*/
327339
static int re_resize(ReCompiled *p, int N){
328340
char *aOp;
329341
int *aArg;
342
+ if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
330343
aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
331
- if( aOp==0 ) return 1;
344
+ if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
332345
p->aOp = aOp;
333346
aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
334
- if( aArg==0 ) return 1;
347
+ if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
335348
p->aArg = aArg;
336349
p->nAlloc = N;
337350
return 0;
338351
}
339352
@@ -468,11 +481,10 @@
468481
const char *zErr;
469482
while( (c = p->xNextChar(&p->sIn))!=0 ){
470483
iStart = p->nState;
471484
switch( c ){
472485
case '|':
473
- case '$':
474486
case ')': {
475487
p->sIn.i--;
476488
return 0;
477489
}
478490
case '(': {
@@ -504,44 +516,61 @@
504516
}
505517
case '?': {
506518
if( iPrev<0 ) return "'?' without operand";
507519
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
508520
break;
521
+ }
522
+ case '$': {
523
+ re_append(p, RE_OP_MATCH, RE_EOF);
524
+ break;
525
+ }
526
+ case '^': {
527
+ re_append(p, RE_OP_ATSTART, 0);
528
+ break;
509529
}
510530
case '{': {
511
- int m = 0, n = 0;
512
- int sz, j;
531
+ unsigned int m = 0, n = 0;
532
+ unsigned int sz, j;
513533
if( iPrev<0 ) return "'{m,n}' without operand";
514
- while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
534
+ while( (c=rePeek(p))>='0' && c<='9' ){
535
+ m = m*10 + c - '0';
536
+ if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
537
+ p->sIn.i++;
538
+ }
515539
n = m;
516540
if( c==',' ){
517541
p->sIn.i++;
518542
n = 0;
519
- while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
543
+ while( (c=rePeek(p))>='0' && c<='9' ){
544
+ n = n*10 + c-'0';
545
+ if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
546
+ p->sIn.i++;
547
+ }
520548
}
521549
if( c!='}' ) return "unmatched '{'";
522
- if( n>0 && n<m ) return "n less than m in '{m,n}'";
550
+ if( n<m ) return "n less than m in '{m,n}'";
523551
p->sIn.i++;
524552
sz = p->nState - iPrev;
525553
if( m==0 ){
526554
if( n==0 ) return "both m and n are zero in '{m,n}'";
527555
re_insert(p, iPrev, RE_OP_FORK, sz+1);
556
+ iPrev++;
528557
n--;
529558
}else{
530559
for(j=1; j<m; j++) re_copy(p, iPrev, sz);
531560
}
532561
for(j=m; j<n; j++){
533562
re_append(p, RE_OP_FORK, sz+1);
534563
re_copy(p, iPrev, sz);
535564
}
536565
if( n==0 && m>0 ){
537
- re_append(p, RE_OP_FORK, -sz);
566
+ re_append(p, RE_OP_FORK, -(int)sz);
538567
}
539568
break;
540569
}
541570
case '[': {
542
- int iFirst = p->nState;
571
+ unsigned int iFirst = p->nState;
543572
if( rePeek(p)=='^' ){
544573
re_append(p, RE_OP_CC_EXC, 0);
545574
p->sIn.i++;
546575
}else{
547576
re_append(p, RE_OP_CC_INC, 0);
@@ -561,11 +590,11 @@
561590
re_append(p, RE_OP_CC_VALUE, c);
562591
}
563592
if( rePeek(p)==']' ){ p->sIn.i++; break; }
564593
}
565594
if( c==0 ) return "unclosed '['";
566
- p->aArg[iFirst] = p->nState - iFirst;
595
+ if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
567596
break;
568597
}
569598
case '\\': {
570599
int specialOp = 0;
571600
switch( rePeek(p) ){
@@ -612,11 +641,16 @@
612641
** Compile a textual regular expression in zIn[] into a compiled regular
613642
** expression suitable for us by re_match() and return a pointer to the
614643
** compiled regular expression in *ppRe. Return NULL on success or an
615644
** error message if something goes wrong.
616645
*/
617
-const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
646
+static const char *re_compile(
647
+ ReCompiled **ppRe, /* OUT: write compiled NFA here */
648
+ const char *zIn, /* Input regular expression */
649
+ int mxRe, /* Complexity limit */
650
+ int noCase /* True for caseless comparisons */
651
+){
618652
ReCompiled *pRe;
619653
const char *zErr;
620654
int i, j;
621655
622656
*ppRe = 0;
@@ -624,13 +658,15 @@
624658
if( pRe==0 ){
625659
return "out of memory";
626660
}
627661
memset(pRe, 0, sizeof(*pRe));
628662
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
663
+ pRe->mxAlloc = mxRe;
629664
if( re_resize(pRe, 30) ){
665
+ zErr = pRe->zErr;
630666
re_free(pRe);
631
- return "out of memory";
667
+ return zErr;
632668
}
633669
if( zIn[0]=='^' ){
634670
zIn++;
635671
}else{
636672
re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -641,15 +677,11 @@
641677
zErr = re_subcompile_re(pRe);
642678
if( zErr ){
643679
re_free(pRe);
644680
return zErr;
645681
}
646
- if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
647
- re_append(pRe, RE_OP_MATCH, RE_EOF);
648
- re_append(pRe, RE_OP_ACCEPT, 0);
649
- *ppRe = pRe;
650
- }else if( pRe->sIn.i>=pRe->sIn.mx ){
682
+ if( pRe->sIn.i>=pRe->sIn.mx ){
651683
re_append(pRe, RE_OP_ACCEPT, 0);
652684
*ppRe = pRe;
653685
}else{
654686
re_free(pRe);
655687
return "unrecognized character";
@@ -658,23 +690,23 @@
658690
/* The following is a performance optimization. If the regex begins with
659691
** ".*" (if the input regex lacks an initial "^") and afterwards there are
660692
** one or more matching characters, enter those matching characters into
661693
** zInit[]. The re_match() routine can then search ahead in the input
662694
** string looking for the initial match without having to run the whole
663
- ** regex engine over the string. Do not worry able trying to match
695
+ ** regex engine over the string. Do not worry about trying to match
664696
** unicode characters beyond plane 0 - those are very rare and this is
665697
** just an optimization. */
666698
if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
667699
for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
668700
unsigned x = pRe->aArg[i];
669
- if( x<=127 ){
701
+ if( x<=0x7f ){
670702
pRe->zInit[j++] = (unsigned char)x;
671
- }else if( x<=0xfff ){
703
+ }else if( x<=0x7ff ){
672704
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
673705
pRe->zInit[j++] = 0x80 | (x&0x3f);
674706
}else if( x<=0xffff ){
675
- pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12));
707
+ pRe->zInit[j++] = (unsigned char)(0xe0 | (x>>12));
676708
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
677709
pRe->zInit[j++] = 0x80 | (x&0x3f);
678710
}else{
679711
break;
680712
}
@@ -682,10 +714,79 @@
682714
if( j>0 && pRe->zInit[j-1]==0 ) j--;
683715
pRe->nInit = j;
684716
}
685717
return pRe->zErr;
686718
}
719
+
720
+/*
721
+** Implementation of the regexp() SQL function. This function implements
722
+** the build-in REGEXP operator. The first argument to the function is the
723
+** pattern and the second argument is the string. So, the SQL statements:
724
+**
725
+** A REGEXP B
726
+**
727
+** is implemented as regexp(B,A).
728
+*/
729
+static void re_sql_func(
730
+ sqlite3_context *context,
731
+ int argc,
732
+ sqlite3_value **argv
733
+){
734
+ ReCompiled *pRe; /* Compiled regular expression */
735
+ const char *zPattern; /* The regular expression */
736
+ const unsigned char *zStr;/* String being searched */
737
+ const char *zErr; /* Compile error message */
738
+ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
739
+
740
+ (void)argc; /* Unused */
741
+ pRe = sqlite3_get_auxdata(context, 0);
742
+ if( pRe==0 ){
743
+ zPattern = (const char*)sqlite3_value_text(argv[0]);
744
+ if( zPattern==0 ) return;
745
+ zErr = fossil_re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
746
+ if( zErr ){
747
+ re_free(pRe);
748
+ /* The original SQLite function from which this code was copied raises
749
+ ** an error if the REGEXP contained a syntax error. This variant
750
+ ** silently fails to match, as that works better for Fossil.
751
+ ** sqlite3_result_error(context, zErr, -1); */
752
+ sqlite3_result_int(context, 0);
753
+ return;
754
+ }
755
+ if( pRe==0 ){
756
+ sqlite3_result_error_nomem(context);
757
+ return;
758
+ }
759
+ setAux = 1;
760
+ }
761
+ zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
762
+ if( zStr!=0 ){
763
+ sqlite3_result_int(context, re_match(pRe, zStr, -1));
764
+ }
765
+ if( setAux ){
766
+ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
767
+ }
768
+}
769
+
770
+/*
771
+** Invoke this routine to register the regexp() function with the
772
+** SQLite database connection.
773
+*/
774
+int re_add_sql_func(sqlite3 *db){
775
+ int rc;
776
+ rc = sqlite3_create_function(db, "regexp", 2,
777
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
778
+ 0, re_sql_func, 0, 0);
779
+ if( rc==SQLITE_OK ){
780
+ /* The regexpi(PATTERN,STRING) function is a case-insensitive version
781
+ ** of regexp(PATTERN,STRING). */
782
+ rc = sqlite3_create_function(db, "regexpi", 2,
783
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
784
+ (void*)db, re_sql_func, 0, 0);
785
+ }
786
+ return rc;
787
+}
687788
688789
/*
689790
** The input zIn is a string that we want to match exactly as part of
690791
** a regular expression. Return a new string (in space obtained from
691792
** fossil_malloc() or the equivalent) that escapes all regexp syntax
@@ -723,71 +824,27 @@
723824
blob_materialize(&out);
724825
return out.aData;
725826
}
726827
727828
/*
728
-** Implementation of the regexp() SQL function. This function implements
729
-** the build-in REGEXP operator. The first argument to the function is the
730
-** pattern and the second argument is the string. So, the SQL statements:
829
+** SETTING: regexp-limit width=8 default=1000
731830
**
732
-** A REGEXP B
733
-**
734
-** is implemented as regexp(B,A).
831
+** Limit the size of the bytecode used to implement a regular expression
832
+** to this many steps. It is important to limit this to avoid possible
833
+** DoS attacks.
834
+*/
835
+
836
+/*
837
+** Compile an RE using re_maxlen().
735838
*/
736
-static void re_sql_func(
737
- sqlite3_context *context,
738
- int argc,
739
- sqlite3_value **argv
839
+const char *fossil_re_compile(
840
+ ReCompiled **ppRe, /* OUT: write compiled NFA here */
841
+ const char *zIn, /* Input regular expression */
842
+ int noCase /* True for caseless comparisons */
740843
){
741
- ReCompiled *pRe; /* Compiled regular expression */
742
- const char *zPattern; /* The regular expression */
743
- const unsigned char *zStr;/* String being searched */
744
- const char *zErr; /* Compile error message */
745
- int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
746
-
747
- (void)argc; /* Unused */
748
- pRe = sqlite3_get_auxdata(context, 0);
749
- if( pRe==0 ){
750
- zPattern = (const char*)sqlite3_value_text(argv[0]);
751
- if( zPattern==0 ) return;
752
- zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
753
- if( zErr ){
754
- re_free(pRe);
755
- sqlite3_result_int(context, 0);
756
- /* sqlite3_result_error(context, zErr, -1); */
757
- return;
758
- }
759
- if( pRe==0 ){
760
- sqlite3_result_error_nomem(context);
761
- return;
762
- }
763
- setAux = 1;
764
- }
765
- zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
766
- if( zStr!=0 ){
767
- sqlite3_result_int(context, re_match(pRe, zStr, -1));
768
- }
769
- if( setAux ){
770
- sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
771
- }
772
-}
773
-
774
-/*
775
-** Invoke this routine to register the regexp() function with the
776
-** SQLite database connection.
777
-*/
778
-int re_add_sql_func(sqlite3 *db){
779
- int rc;
780
- rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
781
- 0, re_sql_func, 0, 0);
782
- if( rc==SQLITE_OK ){
783
- /* The regexpi(PATTERN,STRING) function is a case-insensitive version
784
- ** of regexp(PATTERN,STRING). */
785
- rc = sqlite3_create_function(db, "regexpi", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
786
- (void*)db, re_sql_func, 0, 0);
787
- }
788
- return rc;
844
+ int mxLen = g.db ? db_get_int("regexp-limit",1000) : 1000;
845
+ return re_compile(ppRe, zIn, mxLen, noCase);
789846
}
790847
791848
/*
792849
** Run a "grep" over a single file read from disk.
793850
*/
@@ -850,25 +907,38 @@
850907
** Run a regular expression match over the named disk files, or against
851908
** standard input if no disk files are named on the command-line.
852909
**
853910
** Options:
854911
** -i|--ignore-case Ignore case
912
+** --robot-exception Use the robot-exception setting as the REGEXP
855913
*/
856914
void re_test_grep(void){
857915
ReCompiled *pRe;
858916
const char *zErr;
917
+ int iFileList = 3;
859918
int ignoreCase = find_option("ignore-case","i",0)!=0;
860
- if( g.argc<3 ){
861
- usage("REGEXP [FILE...]");
919
+ int bRobot = find_option("robot-exception",0,0)!=0;
920
+ if( bRobot ){
921
+ const char *zRe;
922
+ db_find_and_open_repository(0,0);
923
+ verify_all_options();
924
+ zRe = db_get("robot-exception","^$");
925
+ zErr = fossil_re_compile(&pRe, zRe, ignoreCase);
926
+ iFileList = 2;
927
+ }else{
928
+ verify_all_options();
929
+ if( g.argc<3 ){
930
+ usage("REGEXP [FILE...]");
931
+ }
932
+ zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
862933
}
863
- zErr = re_compile(&pRe, g.argv[2], ignoreCase);
864934
if( zErr ) fossil_fatal("%s", zErr);
865
- if( g.argc==3 ){
935
+ if( g.argc==iFileList ){
866936
grep_file(pRe, "-", stdin);
867937
}else{
868938
int i;
869
- for(i=3; i<g.argc; i++){
939
+ for(i=iFileList; i<g.argc; i++){
870940
FILE *in = fossil_fopen(g.argv[i], "rb");
871941
if( in==0 ){
872942
fossil_warning("cannot open \"%s\"", g.argv[i]);
873943
}else{
874944
grep_file(pRe, g.argv[i], in);
@@ -939,11 +1009,11 @@
9391009
db_find_and_open_repository(0, 0);
9401010
verify_all_options();
9411011
if( g.argc<4 ){
9421012
usage("REGEXP FILENAME ...");
9431013
}
944
- zErr = re_compile(&pRe, g.argv[2], ignoreCase);
1014
+ zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
9451015
if( zErr ) fossil_fatal("%s", zErr);
9461016
9471017
add_content_sql_commands(g.db);
9481018
db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
9491019
for(ii=3; ii<g.argc; ii++){
@@ -1016,5 +1086,86 @@
10161086
}else{
10171087
fossil_print("%d\n", nMatch);
10181088
}
10191089
}
10201090
}
1091
+
1092
+/*
1093
+** WEBPAGE: re_rules
1094
+**
1095
+** Show a summary of the regular expression matching rules for Fossil.
1096
+*/
1097
+void re_rules_page(void){
1098
+ style_set_current_feature("wiki");
1099
+ style_header("Regular Expression Syntax");
1100
+ @ <p>Syntax rules for regular expression matching in Fossil:</p>
1101
+ @
1102
+ @ <table border="0" cellpadding="0" cellspacing="0">
1103
+ @ <tr><th>&emsp;&emsp;&emsp;<th>Pattern
1104
+ @ <th>&emsp;&emsp;&emsp;<th align="left">Match
1105
+ @ <tr><td><td><i>X</i><b>*</b>
1106
+ @ <td><td>Zero or more occurrences of <i>X</i>
1107
+ @ <tr><td><td><i>X</i><b>+</b>
1108
+ @ <td><td>One or more occurrences of <i>X</i>
1109
+ @ <tr><td><td><i>X</i><b>?</b>
1110
+ @ <td><td>Zero or one occurrences of <i>X</i>
1111
+ @ <tr><td><td><i>X</i><b>{</b><i>P</i><b>,</b><i>Q</i><b>}</b>
1112
+ @ <td><td>Between P and Q occurrences of <i>X</i>
1113
+ @ <tr><td><td><b>(</b><i>X</i><b>)</b>
1114
+ @ <td><td><i>X</i>
1115
+ @ <tr><td><td><i>X</i><b>|</b><i>Y</i>
1116
+ @ <td><td><i>X</i> or <i>Y</i>
1117
+ @ <tr><td><td><b>^</b><i>X</i>
1118
+ @ <td><td><i>X</i> at the beginning of the string
1119
+ @ <tr><td><td><i>X</i><b>$</b>
1120
+ @ <td><td><i>X</i> at the end of the string
1121
+ @ <tr><td><td><b>.</b>
1122
+ @ <td><td>Any single character
1123
+ @ <tr><td><td><b>\</b><i>C</i>
1124
+ @ <td><td>Character <i>C</i> if <i>C</i> is one of: <b>\{}()[]|*+?</b>
1125
+ @ <tr><td><td><b>\</b><i>C</i>
1126
+ @ <td><td>C-language escapes if <i>C</i> is one of: <b>afnrtv</b>
1127
+ @ <tr><td><td><b>\u</b><i>HHHH</i>
1128
+ @ <td><td>Unicode character U+HHHH where <i>HHHH</i> is four hex digits
1129
+ @ <tr><td><td><b>\</b><i>HH</i>
1130
+ @ <td><td>Unicode character U+00HH where <i>HH</i> is two hex digits
1131
+ @ <tr><td><td><b>[</b><i>abc</i><b>]</b>
1132
+ @ <td><td>Any single character from <i>abc</i>
1133
+ @ <tr><td><td><b>[^</b><i>abc</i><b>]</b>
1134
+ @ <td><td>Any single character not in <i>abc</i>
1135
+ @ <tr><td><td><b>[</b><i>a-z</i><b>]</b>
1136
+ @ <td><td>Any single character between <i>a</i> and <i>z</i>, inclusive
1137
+ @ <tr><td><td><b>[^</b><i>a-z</i><b>]</b>
1138
+ @ <td><td>Any single character not between <i>a</i> and <i>z</i>
1139
+ @ <tr><td><td><b>\b</b>
1140
+ @ <td><td>Word boundary
1141
+ @ <tr><td><td><b>\w</b>
1142
+ @ <td><td>A word character: a-zA-Z0-9 or _
1143
+ @ <tr><td><td><b>\W</b>
1144
+ @ <td><td>A non-word character
1145
+ @ <tr><td><td><b>\d</b>
1146
+ @ <td><td>A digit. 0-9
1147
+ @ <tr><td><td><b>\D</b>
1148
+ @ <td><td>A non-digit character
1149
+ @ <tr><td><td><b>\s</b>
1150
+ @ <td><td>A whitespace character
1151
+ @ <tr><td><td><b>\S</b>
1152
+ @ <td><td>A non-whitespace character
1153
+ @ </table>
1154
+ @
1155
+ @ <p>In the "Pattern" column of the table above:</p>
1156
+ @ <ul>
1157
+ @ <li> "<i>X</i>" and "<i>Y</i>" mean any subpattern
1158
+ @ <li> "<i>P</i>" and "<i>Q</i>" mean integers
1159
+ @ <li> "<i>C</i>" means a single character
1160
+ @ <li> "<i>H</i>" means a hexadecimal digit
1161
+ @ <li> "<i>abc</i>" means any sequences of one or more characters
1162
+ @ <li> "<i>a-z</i>" means any single character, a single "<b>-</b>"
1163
+ @ character, and then one additional character.
1164
+ @ <li> All other symbols in the patterns are literal text
1165
+ @ </ul>
1166
+ @
1167
+ @ <p>The "<i>X</i><b>|</b><i>Y</i>" pattern has lower precedence
1168
+ @ than the others. Use "<b>(</b>...<b>)</b>" for grouping, as
1169
+ @ necessary.
1170
+ style_finish_page();
1171
+}
10211172
10221173
ADDED src/robot.c
--- src/regexp.c
+++ src/regexp.c
@@ -53,16 +53,19 @@
53 ** expression and M is the size of the input string. The matcher never
54 ** exhibits exponential behavior. Note that the X{p,q} operator expands
55 ** to p copies of X following by q-p copies of X? and that the size of the
56 ** regular expression in the O(N*M) performance bound is computed after
57 ** this expansion.
 
 
58 */
59 #include "config.h"
60 #include "regexp.h"
61
62 /* The end-of-input character */
63 #define RE_EOF 0 /* End of input */
 
64
65 /* The NFA is implemented as sequence of opcodes taken from the following
66 ** set. Each opcode has a single integer argument.
67 */
68 #define RE_OP_MATCH 1 /* Match the one character in the argument */
@@ -80,10 +83,11 @@
80 #define RE_OP_DIGIT 13 /* digit: [0-9] */
81 #define RE_OP_NOTDIGIT 14 /* Not a digit */
82 #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
83 #define RE_OP_NOTSPACE 16 /* Not a digit */
84 #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
 
85
86 /* Each opcode is a "state" in the NFA */
87 typedef unsigned short ReStateNumber;
88
89 /* Because this is an NFA and not a DFA, multiple states can be active at
@@ -113,13 +117,14 @@
113 const char *zErr; /* Error message to return */
114 char *aOp; /* Operators for the virtual machine */
115 int *aArg; /* Arguments to each operator */
116 unsigned (*xNextChar)(ReInput*); /* Next character function */
117 unsigned char zInit[12]; /* Initial text to match */
118 int nInit; /* Number of characters in zInit */
119 unsigned nState; /* Number of entries in aOp[] and aArg[] */
120 unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
 
121 };
122 #endif
123
124 /* Add a state to the given state set if it is not already there */
125 static void re_add_state(ReStateSet *pSet, int newState){
@@ -144,11 +149,11 @@
144 }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
145 && (p->z[p->i+1]&0xc0)==0x80 ){
146 c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
147 p->i += 2;
148 if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
149 }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
150 && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
151 c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
152 | (p->z[p->i+2]&0x3f);
153 p->i += 3;
154 if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
@@ -185,11 +190,11 @@
185 ReStateSet aStateSet[2], *pThis, *pNext;
186 ReStateNumber aSpace[100];
187 ReStateNumber *pToFree;
188 unsigned int i = 0;
189 unsigned int iSwap = 0;
190 int c = RE_EOF+1;
191 int cPrev = 0;
192 int rc = 0;
193 ReInput in;
194
195 in.z = zIn;
@@ -204,10 +209,11 @@
204 strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
205 ){
206 in.i++;
207 }
208 if( in.i+pRe->nInit>in.mx ) return 0;
 
209 }
210
211 if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
212 pToFree = 0;
213 aStateSet[0].aState = aSpace;
@@ -231,10 +237,14 @@
231 int x = pThis->aState[i];
232 switch( pRe->aOp[x] ){
233 case RE_OP_MATCH: {
234 if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
235 break;
 
 
 
 
236 }
237 case RE_OP_ANY: {
238 if( c!=0 ) re_add_state(pNext, x+1);
239 break;
240 }
@@ -284,13 +294,13 @@
284 rc = 1;
285 goto re_match_end;
286 }
287 case RE_OP_CC_EXC: {
288 if( c==0 ) break;
289 /* fall-through */
290 }
291 case RE_OP_CC_INC: {
292 int j = 1;
293 int n = pRe->aArg[x];
294 int hit = 0;
295 for(j=1; j>0 && j<n; j++){
296 if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
@@ -313,11 +323,13 @@
313 }
314 }
315 }
316 }
317 for(i=0; i<pNext->nState; i++){
318 if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
 
 
319 }
320 re_match_end:
321 fossil_free(pToFree);
322 return rc;
323 }
@@ -325,15 +337,16 @@
325 /* Resize the opcode and argument arrays for an RE under construction.
326 */
327 static int re_resize(ReCompiled *p, int N){
328 char *aOp;
329 int *aArg;
 
330 aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
331 if( aOp==0 ) return 1;
332 p->aOp = aOp;
333 aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
334 if( aArg==0 ) return 1;
335 p->aArg = aArg;
336 p->nAlloc = N;
337 return 0;
338 }
339
@@ -468,11 +481,10 @@
468 const char *zErr;
469 while( (c = p->xNextChar(&p->sIn))!=0 ){
470 iStart = p->nState;
471 switch( c ){
472 case '|':
473 case '$':
474 case ')': {
475 p->sIn.i--;
476 return 0;
477 }
478 case '(': {
@@ -504,44 +516,61 @@
504 }
505 case '?': {
506 if( iPrev<0 ) return "'?' without operand";
507 re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
508 break;
 
 
 
 
 
 
 
 
509 }
510 case '{': {
511 int m = 0, n = 0;
512 int sz, j;
513 if( iPrev<0 ) return "'{m,n}' without operand";
514 while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
 
 
 
 
515 n = m;
516 if( c==',' ){
517 p->sIn.i++;
518 n = 0;
519 while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
 
 
 
 
520 }
521 if( c!='}' ) return "unmatched '{'";
522 if( n>0 && n<m ) return "n less than m in '{m,n}'";
523 p->sIn.i++;
524 sz = p->nState - iPrev;
525 if( m==0 ){
526 if( n==0 ) return "both m and n are zero in '{m,n}'";
527 re_insert(p, iPrev, RE_OP_FORK, sz+1);
 
528 n--;
529 }else{
530 for(j=1; j<m; j++) re_copy(p, iPrev, sz);
531 }
532 for(j=m; j<n; j++){
533 re_append(p, RE_OP_FORK, sz+1);
534 re_copy(p, iPrev, sz);
535 }
536 if( n==0 && m>0 ){
537 re_append(p, RE_OP_FORK, -sz);
538 }
539 break;
540 }
541 case '[': {
542 int iFirst = p->nState;
543 if( rePeek(p)=='^' ){
544 re_append(p, RE_OP_CC_EXC, 0);
545 p->sIn.i++;
546 }else{
547 re_append(p, RE_OP_CC_INC, 0);
@@ -561,11 +590,11 @@
561 re_append(p, RE_OP_CC_VALUE, c);
562 }
563 if( rePeek(p)==']' ){ p->sIn.i++; break; }
564 }
565 if( c==0 ) return "unclosed '['";
566 p->aArg[iFirst] = p->nState - iFirst;
567 break;
568 }
569 case '\\': {
570 int specialOp = 0;
571 switch( rePeek(p) ){
@@ -612,11 +641,16 @@
612 ** Compile a textual regular expression in zIn[] into a compiled regular
613 ** expression suitable for us by re_match() and return a pointer to the
614 ** compiled regular expression in *ppRe. Return NULL on success or an
615 ** error message if something goes wrong.
616 */
617 const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
 
 
 
 
 
618 ReCompiled *pRe;
619 const char *zErr;
620 int i, j;
621
622 *ppRe = 0;
@@ -624,13 +658,15 @@
624 if( pRe==0 ){
625 return "out of memory";
626 }
627 memset(pRe, 0, sizeof(*pRe));
628 pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
 
629 if( re_resize(pRe, 30) ){
 
630 re_free(pRe);
631 return "out of memory";
632 }
633 if( zIn[0]=='^' ){
634 zIn++;
635 }else{
636 re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -641,15 +677,11 @@
641 zErr = re_subcompile_re(pRe);
642 if( zErr ){
643 re_free(pRe);
644 return zErr;
645 }
646 if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
647 re_append(pRe, RE_OP_MATCH, RE_EOF);
648 re_append(pRe, RE_OP_ACCEPT, 0);
649 *ppRe = pRe;
650 }else if( pRe->sIn.i>=pRe->sIn.mx ){
651 re_append(pRe, RE_OP_ACCEPT, 0);
652 *ppRe = pRe;
653 }else{
654 re_free(pRe);
655 return "unrecognized character";
@@ -658,23 +690,23 @@
658 /* The following is a performance optimization. If the regex begins with
659 ** ".*" (if the input regex lacks an initial "^") and afterwards there are
660 ** one or more matching characters, enter those matching characters into
661 ** zInit[]. The re_match() routine can then search ahead in the input
662 ** string looking for the initial match without having to run the whole
663 ** regex engine over the string. Do not worry able trying to match
664 ** unicode characters beyond plane 0 - those are very rare and this is
665 ** just an optimization. */
666 if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
667 for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
668 unsigned x = pRe->aArg[i];
669 if( x<=127 ){
670 pRe->zInit[j++] = (unsigned char)x;
671 }else if( x<=0xfff ){
672 pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
673 pRe->zInit[j++] = 0x80 | (x&0x3f);
674 }else if( x<=0xffff ){
675 pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12));
676 pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
677 pRe->zInit[j++] = 0x80 | (x&0x3f);
678 }else{
679 break;
680 }
@@ -682,10 +714,79 @@
682 if( j>0 && pRe->zInit[j-1]==0 ) j--;
683 pRe->nInit = j;
684 }
685 return pRe->zErr;
686 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
688 /*
689 ** The input zIn is a string that we want to match exactly as part of
690 ** a regular expression. Return a new string (in space obtained from
691 ** fossil_malloc() or the equivalent) that escapes all regexp syntax
@@ -723,71 +824,27 @@
723 blob_materialize(&out);
724 return out.aData;
725 }
726
727 /*
728 ** Implementation of the regexp() SQL function. This function implements
729 ** the build-in REGEXP operator. The first argument to the function is the
730 ** pattern and the second argument is the string. So, the SQL statements:
731 **
732 ** A REGEXP B
733 **
734 ** is implemented as regexp(B,A).
 
 
 
 
735 */
736 static void re_sql_func(
737 sqlite3_context *context,
738 int argc,
739 sqlite3_value **argv
740 ){
741 ReCompiled *pRe; /* Compiled regular expression */
742 const char *zPattern; /* The regular expression */
743 const unsigned char *zStr;/* String being searched */
744 const char *zErr; /* Compile error message */
745 int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
746
747 (void)argc; /* Unused */
748 pRe = sqlite3_get_auxdata(context, 0);
749 if( pRe==0 ){
750 zPattern = (const char*)sqlite3_value_text(argv[0]);
751 if( zPattern==0 ) return;
752 zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
753 if( zErr ){
754 re_free(pRe);
755 sqlite3_result_int(context, 0);
756 /* sqlite3_result_error(context, zErr, -1); */
757 return;
758 }
759 if( pRe==0 ){
760 sqlite3_result_error_nomem(context);
761 return;
762 }
763 setAux = 1;
764 }
765 zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
766 if( zStr!=0 ){
767 sqlite3_result_int(context, re_match(pRe, zStr, -1));
768 }
769 if( setAux ){
770 sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
771 }
772 }
773
774 /*
775 ** Invoke this routine to register the regexp() function with the
776 ** SQLite database connection.
777 */
778 int re_add_sql_func(sqlite3 *db){
779 int rc;
780 rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
781 0, re_sql_func, 0, 0);
782 if( rc==SQLITE_OK ){
783 /* The regexpi(PATTERN,STRING) function is a case-insensitive version
784 ** of regexp(PATTERN,STRING). */
785 rc = sqlite3_create_function(db, "regexpi", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
786 (void*)db, re_sql_func, 0, 0);
787 }
788 return rc;
789 }
790
791 /*
792 ** Run a "grep" over a single file read from disk.
793 */
@@ -850,25 +907,38 @@
850 ** Run a regular expression match over the named disk files, or against
851 ** standard input if no disk files are named on the command-line.
852 **
853 ** Options:
854 ** -i|--ignore-case Ignore case
 
855 */
856 void re_test_grep(void){
857 ReCompiled *pRe;
858 const char *zErr;
 
859 int ignoreCase = find_option("ignore-case","i",0)!=0;
860 if( g.argc<3 ){
861 usage("REGEXP [FILE...]");
 
 
 
 
 
 
 
 
 
 
 
 
862 }
863 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
864 if( zErr ) fossil_fatal("%s", zErr);
865 if( g.argc==3 ){
866 grep_file(pRe, "-", stdin);
867 }else{
868 int i;
869 for(i=3; i<g.argc; i++){
870 FILE *in = fossil_fopen(g.argv[i], "rb");
871 if( in==0 ){
872 fossil_warning("cannot open \"%s\"", g.argv[i]);
873 }else{
874 grep_file(pRe, g.argv[i], in);
@@ -939,11 +1009,11 @@
939 db_find_and_open_repository(0, 0);
940 verify_all_options();
941 if( g.argc<4 ){
942 usage("REGEXP FILENAME ...");
943 }
944 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
945 if( zErr ) fossil_fatal("%s", zErr);
946
947 add_content_sql_commands(g.db);
948 db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
949 for(ii=3; ii<g.argc; ii++){
@@ -1016,5 +1086,86 @@
1016 }else{
1017 fossil_print("%d\n", nMatch);
1018 }
1019 }
1020 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1021
1022 DDED src/robot.c
--- src/regexp.c
+++ src/regexp.c
@@ -53,16 +53,19 @@
53 ** expression and M is the size of the input string. The matcher never
54 ** exhibits exponential behavior. Note that the X{p,q} operator expands
55 ** to p copies of X following by q-p copies of X? and that the size of the
56 ** regular expression in the O(N*M) performance bound is computed after
57 ** this expansion.
58 **
59 ** To help prevent DoS attacks, the maximum size of the NFA is restricted.
60 */
61 #include "config.h"
62 #include "regexp.h"
63
64 /* The end-of-input character */
65 #define RE_EOF 0 /* End of input */
66 #define RE_START 0xfffffff /* Start of input - larger than an UTF-8 */
67
68 /* The NFA is implemented as sequence of opcodes taken from the following
69 ** set. Each opcode has a single integer argument.
70 */
71 #define RE_OP_MATCH 1 /* Match the one character in the argument */
@@ -80,10 +83,11 @@
83 #define RE_OP_DIGIT 13 /* digit: [0-9] */
84 #define RE_OP_NOTDIGIT 14 /* Not a digit */
85 #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
86 #define RE_OP_NOTSPACE 16 /* Not a digit */
87 #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
88 #define RE_OP_ATSTART 18 /* Currently at the start of the string */
89
90 /* Each opcode is a "state" in the NFA */
91 typedef unsigned short ReStateNumber;
92
93 /* Because this is an NFA and not a DFA, multiple states can be active at
@@ -113,13 +117,14 @@
117 const char *zErr; /* Error message to return */
118 char *aOp; /* Operators for the virtual machine */
119 int *aArg; /* Arguments to each operator */
120 unsigned (*xNextChar)(ReInput*); /* Next character function */
121 unsigned char zInit[12]; /* Initial text to match */
122 int nInit; /* Number of bytes in zInit */
123 unsigned nState; /* Number of entries in aOp[] and aArg[] */
124 unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
125 unsigned mxAlloc; /* Complexity limit */
126 };
127 #endif
128
129 /* Add a state to the given state set if it is not already there */
130 static void re_add_state(ReStateSet *pSet, int newState){
@@ -144,11 +149,11 @@
149 }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
150 && (p->z[p->i+1]&0xc0)==0x80 ){
151 c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
152 p->i += 2;
153 if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
154 }else if( (c&0xf8)==0xf0 && p->i+2<p->mx && (p->z[p->i]&0xc0)==0x80
155 && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
156 c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
157 | (p->z[p->i+2]&0x3f);
158 p->i += 3;
159 if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
@@ -185,11 +190,11 @@
190 ReStateSet aStateSet[2], *pThis, *pNext;
191 ReStateNumber aSpace[100];
192 ReStateNumber *pToFree;
193 unsigned int i = 0;
194 unsigned int iSwap = 0;
195 int c = RE_START;
196 int cPrev = 0;
197 int rc = 0;
198 ReInput in;
199
200 in.z = zIn;
@@ -204,10 +209,11 @@
209 strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
210 ){
211 in.i++;
212 }
213 if( in.i+pRe->nInit>in.mx ) return 0;
214 c = RE_START-1;
215 }
216
217 if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
218 pToFree = 0;
219 aStateSet[0].aState = aSpace;
@@ -231,10 +237,14 @@
237 int x = pThis->aState[i];
238 switch( pRe->aOp[x] ){
239 case RE_OP_MATCH: {
240 if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
241 break;
242 }
243 case RE_OP_ATSTART: {
244 if( cPrev==RE_START ) re_add_state(pThis, x+1);
245 break;
246 }
247 case RE_OP_ANY: {
248 if( c!=0 ) re_add_state(pNext, x+1);
249 break;
250 }
@@ -284,13 +294,13 @@
294 rc = 1;
295 goto re_match_end;
296 }
297 case RE_OP_CC_EXC: {
298 if( c==0 ) break;
299 /* fall-through */ goto re_op_cc_inc;
300 }
301 case RE_OP_CC_INC: re_op_cc_inc: {
302 int j = 1;
303 int n = pRe->aArg[x];
304 int hit = 0;
305 for(j=1; j>0 && j<n; j++){
306 if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
@@ -313,11 +323,13 @@
323 }
324 }
325 }
326 }
327 for(i=0; i<pNext->nState; i++){
328 int x = pNext->aState[i];
329 while( pRe->aOp[x]==RE_OP_GOTO ) x += pRe->aArg[x];
330 if( pRe->aOp[x]==RE_OP_ACCEPT ){ rc = 1; break; }
331 }
332 re_match_end:
333 fossil_free(pToFree);
334 return rc;
335 }
@@ -325,15 +337,16 @@
337 /* Resize the opcode and argument arrays for an RE under construction.
338 */
339 static int re_resize(ReCompiled *p, int N){
340 char *aOp;
341 int *aArg;
342 if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
343 aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
344 if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
345 p->aOp = aOp;
346 aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
347 if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
348 p->aArg = aArg;
349 p->nAlloc = N;
350 return 0;
351 }
352
@@ -468,11 +481,10 @@
481 const char *zErr;
482 while( (c = p->xNextChar(&p->sIn))!=0 ){
483 iStart = p->nState;
484 switch( c ){
485 case '|':
 
486 case ')': {
487 p->sIn.i--;
488 return 0;
489 }
490 case '(': {
@@ -504,44 +516,61 @@
516 }
517 case '?': {
518 if( iPrev<0 ) return "'?' without operand";
519 re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
520 break;
521 }
522 case '$': {
523 re_append(p, RE_OP_MATCH, RE_EOF);
524 break;
525 }
526 case '^': {
527 re_append(p, RE_OP_ATSTART, 0);
528 break;
529 }
530 case '{': {
531 unsigned int m = 0, n = 0;
532 unsigned int sz, j;
533 if( iPrev<0 ) return "'{m,n}' without operand";
534 while( (c=rePeek(p))>='0' && c<='9' ){
535 m = m*10 + c - '0';
536 if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
537 p->sIn.i++;
538 }
539 n = m;
540 if( c==',' ){
541 p->sIn.i++;
542 n = 0;
543 while( (c=rePeek(p))>='0' && c<='9' ){
544 n = n*10 + c-'0';
545 if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
546 p->sIn.i++;
547 }
548 }
549 if( c!='}' ) return "unmatched '{'";
550 if( n<m ) return "n less than m in '{m,n}'";
551 p->sIn.i++;
552 sz = p->nState - iPrev;
553 if( m==0 ){
554 if( n==0 ) return "both m and n are zero in '{m,n}'";
555 re_insert(p, iPrev, RE_OP_FORK, sz+1);
556 iPrev++;
557 n--;
558 }else{
559 for(j=1; j<m; j++) re_copy(p, iPrev, sz);
560 }
561 for(j=m; j<n; j++){
562 re_append(p, RE_OP_FORK, sz+1);
563 re_copy(p, iPrev, sz);
564 }
565 if( n==0 && m>0 ){
566 re_append(p, RE_OP_FORK, -(int)sz);
567 }
568 break;
569 }
570 case '[': {
571 unsigned int iFirst = p->nState;
572 if( rePeek(p)=='^' ){
573 re_append(p, RE_OP_CC_EXC, 0);
574 p->sIn.i++;
575 }else{
576 re_append(p, RE_OP_CC_INC, 0);
@@ -561,11 +590,11 @@
590 re_append(p, RE_OP_CC_VALUE, c);
591 }
592 if( rePeek(p)==']' ){ p->sIn.i++; break; }
593 }
594 if( c==0 ) return "unclosed '['";
595 if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
596 break;
597 }
598 case '\\': {
599 int specialOp = 0;
600 switch( rePeek(p) ){
@@ -612,11 +641,16 @@
641 ** Compile a textual regular expression in zIn[] into a compiled regular
642 ** expression suitable for us by re_match() and return a pointer to the
643 ** compiled regular expression in *ppRe. Return NULL on success or an
644 ** error message if something goes wrong.
645 */
646 static const char *re_compile(
647 ReCompiled **ppRe, /* OUT: write compiled NFA here */
648 const char *zIn, /* Input regular expression */
649 int mxRe, /* Complexity limit */
650 int noCase /* True for caseless comparisons */
651 ){
652 ReCompiled *pRe;
653 const char *zErr;
654 int i, j;
655
656 *ppRe = 0;
@@ -624,13 +658,15 @@
658 if( pRe==0 ){
659 return "out of memory";
660 }
661 memset(pRe, 0, sizeof(*pRe));
662 pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
663 pRe->mxAlloc = mxRe;
664 if( re_resize(pRe, 30) ){
665 zErr = pRe->zErr;
666 re_free(pRe);
667 return zErr;
668 }
669 if( zIn[0]=='^' ){
670 zIn++;
671 }else{
672 re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -641,15 +677,11 @@
677 zErr = re_subcompile_re(pRe);
678 if( zErr ){
679 re_free(pRe);
680 return zErr;
681 }
682 if( pRe->sIn.i>=pRe->sIn.mx ){
 
 
 
 
683 re_append(pRe, RE_OP_ACCEPT, 0);
684 *ppRe = pRe;
685 }else{
686 re_free(pRe);
687 return "unrecognized character";
@@ -658,23 +690,23 @@
690 /* The following is a performance optimization. If the regex begins with
691 ** ".*" (if the input regex lacks an initial "^") and afterwards there are
692 ** one or more matching characters, enter those matching characters into
693 ** zInit[]. The re_match() routine can then search ahead in the input
694 ** string looking for the initial match without having to run the whole
695 ** regex engine over the string. Do not worry about trying to match
696 ** unicode characters beyond plane 0 - those are very rare and this is
697 ** just an optimization. */
698 if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
699 for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
700 unsigned x = pRe->aArg[i];
701 if( x<=0x7f ){
702 pRe->zInit[j++] = (unsigned char)x;
703 }else if( x<=0x7ff ){
704 pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
705 pRe->zInit[j++] = 0x80 | (x&0x3f);
706 }else if( x<=0xffff ){
707 pRe->zInit[j++] = (unsigned char)(0xe0 | (x>>12));
708 pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
709 pRe->zInit[j++] = 0x80 | (x&0x3f);
710 }else{
711 break;
712 }
@@ -682,10 +714,79 @@
714 if( j>0 && pRe->zInit[j-1]==0 ) j--;
715 pRe->nInit = j;
716 }
717 return pRe->zErr;
718 }
719
720 /*
721 ** Implementation of the regexp() SQL function. This function implements
722 ** the build-in REGEXP operator. The first argument to the function is the
723 ** pattern and the second argument is the string. So, the SQL statements:
724 **
725 ** A REGEXP B
726 **
727 ** is implemented as regexp(B,A).
728 */
729 static void re_sql_func(
730 sqlite3_context *context,
731 int argc,
732 sqlite3_value **argv
733 ){
734 ReCompiled *pRe; /* Compiled regular expression */
735 const char *zPattern; /* The regular expression */
736 const unsigned char *zStr;/* String being searched */
737 const char *zErr; /* Compile error message */
738 int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
739
740 (void)argc; /* Unused */
741 pRe = sqlite3_get_auxdata(context, 0);
742 if( pRe==0 ){
743 zPattern = (const char*)sqlite3_value_text(argv[0]);
744 if( zPattern==0 ) return;
745 zErr = fossil_re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
746 if( zErr ){
747 re_free(pRe);
748 /* The original SQLite function from which this code was copied raises
749 ** an error if the REGEXP contained a syntax error. This variant
750 ** silently fails to match, as that works better for Fossil.
751 ** sqlite3_result_error(context, zErr, -1); */
752 sqlite3_result_int(context, 0);
753 return;
754 }
755 if( pRe==0 ){
756 sqlite3_result_error_nomem(context);
757 return;
758 }
759 setAux = 1;
760 }
761 zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
762 if( zStr!=0 ){
763 sqlite3_result_int(context, re_match(pRe, zStr, -1));
764 }
765 if( setAux ){
766 sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
767 }
768 }
769
770 /*
771 ** Invoke this routine to register the regexp() function with the
772 ** SQLite database connection.
773 */
774 int re_add_sql_func(sqlite3 *db){
775 int rc;
776 rc = sqlite3_create_function(db, "regexp", 2,
777 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
778 0, re_sql_func, 0, 0);
779 if( rc==SQLITE_OK ){
780 /* The regexpi(PATTERN,STRING) function is a case-insensitive version
781 ** of regexp(PATTERN,STRING). */
782 rc = sqlite3_create_function(db, "regexpi", 2,
783 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
784 (void*)db, re_sql_func, 0, 0);
785 }
786 return rc;
787 }
788
789 /*
790 ** The input zIn is a string that we want to match exactly as part of
791 ** a regular expression. Return a new string (in space obtained from
792 ** fossil_malloc() or the equivalent) that escapes all regexp syntax
@@ -723,71 +824,27 @@
824 blob_materialize(&out);
825 return out.aData;
826 }
827
828 /*
829 ** SETTING: regexp-limit width=8 default=1000
 
 
830 **
831 ** Limit the size of the bytecode used to implement a regular expression
832 ** to this many steps. It is important to limit this to avoid possible
833 ** DoS attacks.
834 */
835
836 /*
837 ** Compile an RE using re_maxlen().
838 */
839 const char *fossil_re_compile(
840 ReCompiled **ppRe, /* OUT: write compiled NFA here */
841 const char *zIn, /* Input regular expression */
842 int noCase /* True for caseless comparisons */
843 ){
844 int mxLen = g.db ? db_get_int("regexp-limit",1000) : 1000;
845 return re_compile(ppRe, zIn, mxLen, noCase);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846 }
847
848 /*
849 ** Run a "grep" over a single file read from disk.
850 */
@@ -850,25 +907,38 @@
907 ** Run a regular expression match over the named disk files, or against
908 ** standard input if no disk files are named on the command-line.
909 **
910 ** Options:
911 ** -i|--ignore-case Ignore case
912 ** --robot-exception Use the robot-exception setting as the REGEXP
913 */
914 void re_test_grep(void){
915 ReCompiled *pRe;
916 const char *zErr;
917 int iFileList = 3;
918 int ignoreCase = find_option("ignore-case","i",0)!=0;
919 int bRobot = find_option("robot-exception",0,0)!=0;
920 if( bRobot ){
921 const char *zRe;
922 db_find_and_open_repository(0,0);
923 verify_all_options();
924 zRe = db_get("robot-exception","^$");
925 zErr = fossil_re_compile(&pRe, zRe, ignoreCase);
926 iFileList = 2;
927 }else{
928 verify_all_options();
929 if( g.argc<3 ){
930 usage("REGEXP [FILE...]");
931 }
932 zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
933 }
 
934 if( zErr ) fossil_fatal("%s", zErr);
935 if( g.argc==iFileList ){
936 grep_file(pRe, "-", stdin);
937 }else{
938 int i;
939 for(i=iFileList; i<g.argc; i++){
940 FILE *in = fossil_fopen(g.argv[i], "rb");
941 if( in==0 ){
942 fossil_warning("cannot open \"%s\"", g.argv[i]);
943 }else{
944 grep_file(pRe, g.argv[i], in);
@@ -939,11 +1009,11 @@
1009 db_find_and_open_repository(0, 0);
1010 verify_all_options();
1011 if( g.argc<4 ){
1012 usage("REGEXP FILENAME ...");
1013 }
1014 zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
1015 if( zErr ) fossil_fatal("%s", zErr);
1016
1017 add_content_sql_commands(g.db);
1018 db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
1019 for(ii=3; ii<g.argc; ii++){
@@ -1016,5 +1086,86 @@
1086 }else{
1087 fossil_print("%d\n", nMatch);
1088 }
1089 }
1090 }
1091
1092 /*
1093 ** WEBPAGE: re_rules
1094 **
1095 ** Show a summary of the regular expression matching rules for Fossil.
1096 */
1097 void re_rules_page(void){
1098 style_set_current_feature("wiki");
1099 style_header("Regular Expression Syntax");
1100 @ <p>Syntax rules for regular expression matching in Fossil:</p>
1101 @
1102 @ <table border="0" cellpadding="0" cellspacing="0">
1103 @ <tr><th>&emsp;&emsp;&emsp;<th>Pattern
1104 @ <th>&emsp;&emsp;&emsp;<th align="left">Match
1105 @ <tr><td><td><i>X</i><b>*</b>
1106 @ <td><td>Zero or more occurrences of <i>X</i>
1107 @ <tr><td><td><i>X</i><b>+</b>
1108 @ <td><td>One or more occurrences of <i>X</i>
1109 @ <tr><td><td><i>X</i><b>?</b>
1110 @ <td><td>Zero or one occurrences of <i>X</i>
1111 @ <tr><td><td><i>X</i><b>{</b><i>P</i><b>,</b><i>Q</i><b>}</b>
1112 @ <td><td>Between P and Q occurrences of <i>X</i>
1113 @ <tr><td><td><b>(</b><i>X</i><b>)</b>
1114 @ <td><td><i>X</i>
1115 @ <tr><td><td><i>X</i><b>|</b><i>Y</i>
1116 @ <td><td><i>X</i> or <i>Y</i>
1117 @ <tr><td><td><b>^</b><i>X</i>
1118 @ <td><td><i>X</i> at the beginning of the string
1119 @ <tr><td><td><i>X</i><b>$</b>
1120 @ <td><td><i>X</i> at the end of the string
1121 @ <tr><td><td><b>.</b>
1122 @ <td><td>Any single character
1123 @ <tr><td><td><b>\</b><i>C</i>
1124 @ <td><td>Character <i>C</i> if <i>C</i> is one of: <b>\{}()[]|*+?</b>
1125 @ <tr><td><td><b>\</b><i>C</i>
1126 @ <td><td>C-language escapes if <i>C</i> is one of: <b>afnrtv</b>
1127 @ <tr><td><td><b>\u</b><i>HHHH</i>
1128 @ <td><td>Unicode character U+HHHH where <i>HHHH</i> is four hex digits
1129 @ <tr><td><td><b>\</b><i>HH</i>
1130 @ <td><td>Unicode character U+00HH where <i>HH</i> is two hex digits
1131 @ <tr><td><td><b>[</b><i>abc</i><b>]</b>
1132 @ <td><td>Any single character from <i>abc</i>
1133 @ <tr><td><td><b>[^</b><i>abc</i><b>]</b>
1134 @ <td><td>Any single character not in <i>abc</i>
1135 @ <tr><td><td><b>[</b><i>a-z</i><b>]</b>
1136 @ <td><td>Any single character between <i>a</i> and <i>z</i>, inclusive
1137 @ <tr><td><td><b>[^</b><i>a-z</i><b>]</b>
1138 @ <td><td>Any single character not between <i>a</i> and <i>z</i>
1139 @ <tr><td><td><b>\b</b>
1140 @ <td><td>Word boundary
1141 @ <tr><td><td><b>\w</b>
1142 @ <td><td>A word character: a-zA-Z0-9 or _
1143 @ <tr><td><td><b>\W</b>
1144 @ <td><td>A non-word character
1145 @ <tr><td><td><b>\d</b>
1146 @ <td><td>A digit. 0-9
1147 @ <tr><td><td><b>\D</b>
1148 @ <td><td>A non-digit character
1149 @ <tr><td><td><b>\s</b>
1150 @ <td><td>A whitespace character
1151 @ <tr><td><td><b>\S</b>
1152 @ <td><td>A non-whitespace character
1153 @ </table>
1154 @
1155 @ <p>In the "Pattern" column of the table above:</p>
1156 @ <ul>
1157 @ <li> "<i>X</i>" and "<i>Y</i>" mean any subpattern
1158 @ <li> "<i>P</i>" and "<i>Q</i>" mean integers
1159 @ <li> "<i>C</i>" means a single character
1160 @ <li> "<i>H</i>" means a hexadecimal digit
1161 @ <li> "<i>abc</i>" means any sequences of one or more characters
1162 @ <li> "<i>a-z</i>" means any single character, a single "<b>-</b>"
1163 @ character, and then one additional character.
1164 @ <li> All other symbols in the patterns are literal text
1165 @ </ul>
1166 @
1167 @ <p>The "<i>X</i><b>|</b><i>Y</i>" pattern has lower precedence
1168 @ than the others. Use "<b>(</b>...<b>)</b>" for grouping, as
1169 @ necessary.
1170 style_finish_page();
1171 }
1172
1173 DDED src/robot.c
+410
--- a/src/robot.c
+++ b/src/robot.c
@@ -0,0 +1,410 @@
1
+t to treat as human. Allow it through and also set the robot cookie.
2
+ */
3
+ z = P("token");
4
+ if( z!=0 ){
5
+ if( db_exists("SELECT 1 FROM config"
6
+ " WHERE name='token-%q'"
7
+ " AND json_valid(value,6)"
8
+ " AND value->>'user' IS NOT NULL", z)
9
+ ){
10
+ char *zVal;
11
+ robot_pow_hash();
12
+ zVal = mprintf("%u", robot.h1);
13
+ cgi_set_cookie(ROBOT_COOKIE,zVal,"/",900);
14
+ fossil_free(zVal);
15
+ robot.resultCache = KNOWN_NOT_ROBOT;
16
+ return 0; /* There is a valid token= query parameter */
17
+ }
18
+ cgi_tag_query_parameter("token");
19
+ }
20
+
21
+ /* We have no proof that the request is coming from an interactive
22
+ ** human session, so assume the request comes from a robot.
23
+ */
24
+ robot.resultCache = MIGHT_BE_ROBOT;
25
+ return 1;
26
+}
27
+
28
+/*
29
+** Rewrite the current page with content that attempts
30
+** to prove that the client is not a robot.
31
+*/
32
+static void ask_for_proof_that_client_is_not_robot(void){
33
+ unsigned p1, p2, p3, p4, p5, k2, k3;
34
+ int k;
35
+
36
+ /* Ask the client to present proof-of-work */
37
+ cgi_reset_content();
38
+ cgi_set_content_type("text/html");
39
+ style_header("Browser Verification");
40
+ @ <h1 id="x1">Checking to see if you are a robot<span id="x2"></span></h1>
41
+ @ <form method="GET" id="x6"><p>
42
+ @ <span id="x3" style="visibility:hidden;">\
43
+ @ Press <input type="submit" id="x5" value="Ok" focus> to continue</span>
44
+ @ <span id="x7" style="visibility:hidden;">You appear to be a robot.</span>\
45
+ @ </p>
46
+ if( g.zExtra && g.zExtra[0] ) cgi_tag_query_parameter("name");
47
+ cgi_query_parameters_to_hidden();
48
+ @ <input id="x4" type="hidden" name="proof" value="0">
49
+ @ </form>
50
+ @ <script nonce='%s(style_nonce())'>
51
+ @ function aaa(x){return document.getElementById(x);The "diff" tagge name. The "diff" tag
52
+*
53
+** /vpatch. Thefdiff, and /vpatch. The
54
+**
55
+** also coversaise. "zip" also covers
56
+** /tarba
57
+** then itaracter appended then it
58
+** only
59
+** particularly difficult to compute. In all other case, the tag should
60
+***
61
+** Usually the tag should exaUseful "X" t
62
+** "zipX". See to disable all robot restrictions.
63
+*/
64
+/*
65
+** SETTING: robot-exception width=40 block-text
66
+**
67
+** The value of this setting should be a regular expression.
68
+** If it matches the REQUEST_URI without the SCRIPT_NAME prefix
69
+** matches this regular expression, then the request is an exception
70
+** to anti-robot defenses and should be allowed through. For
71
+** example, to allow robots to download tarballs or ZIP archives
72
+** for named versions and releases, you could use an expression like
73
+** this:
74
+**
75
+** ^/tarball/(version-[0-9.]+|release)/
76
+**
77
+** This setting can hold multiple regular expressions, one
78
+** regular expression per line. The input URL is exempted from
79
+** anti-robot defenses if any of the multiple regular expressions
80
+** matches.
81
+*/
82
+/*
83
+** SETTING: robot-zip-leaf boolean
84
+**
85
+** If this setting is true, the robots are allowed to download tarballs,
86
+** ZIP-archives, and SQL-archives even though "zipX" is found in
87
+** the [[robot-restrict]] setting as long as the specific check-in being
88
+** downloaded is a leaf check-in.
89
+*/
90
+/*
91
+** SETTING: robot-zip-tag width=40 block-text
92
+**
93
+** If this setting is a list of GLOB patterns matching tags,
94
+** then robots are allowed to download tarballs, ZIP-archives, and
95
+** SQL-archives even though "zipX" appears in [[robot-restrict]], as long as
96
+** the specific check-in being downloaded has a tags that matches
97
+** the GLOB list of this setting. Recommended value:
98
+** "release,robot-access".
99
+*/
100
+
101
+/*
102
+** Return the default restriction GLOB
103
+*/
104
+const char *robot_restrict_default(void){
105
+ /* NOTE: The default value is also mentioned in the online help screen of
106
+ ** the "robot-restrict" setting, and in the www/antibot.wiki document. */
107
+ return "timelineX,diff,annotate,fileage,file,finfo,reports,"
108
+ "tree,hexdump,download";
109
+}
110
+
111
+/*
112
+** Return true if zTag matches one of the tags in the rT_URI without the SCRIPT_NAME prefix
113
+** matches this regular expression, then the request is an exception
114
+** to anti-robot defenses and should be allowed through. For
115
+** example, to allow robots to download tarballs or ZIP archives
116
+** for named versions and releases, you could]+|release)/
117
+**
118
+** This setting can hold multiple regular expressions, one
119
+** regular expression per line. The input URL is exempted from
120
+** anti-robot defenses if any of the multiple regular expressions
121
+** matches.
122
+*/
123
+/*
124
+** SETTING: robot-zip-leaf boolean
125
+**
126
+** If this setting is true, the robots are allowed to download tarballs,
127
+** ZIP-archives, and SQL-archives even though "zipX" is found in
128
+** the [[robot-restrict]] setting as long as the specific check-in being
129
+** downloaded is a leaf check-in.
130
+*/
131
+/*
132
+** SETTING: robot-zip-tag width=40 block-text
133
+**
134
+** If this setting is a list of GLOB patterns matching tags,
135
+** then robots are allowed to download tarballs, ZIP-archives, and
136
+** SQL-archives even though "zipX" appears in [[ro downloaded has a tags that matches
137
+** the GLOB list of this setting. Recommended value:
138
+** "release,robot-access".
139
+*/
140
+
141
+/*
142
+** Return the default restriction GLOB
143
+*/
144
+const char *robot_restrict_default(void){
145
+ /* NOTE: The default value is also mentioned in the online help screen of
146
+ ** the "robot-restrict" setting, and in the www/antibot.wiki document. */
147
+ return "timelineX,diff,annotate,fileage,file,finfo,reports,"
148
+ "tree,hexdump,download";
149
+}
150
+
151
+/*
152
+** Return true if zTag matches one of the tags in the robot-restrict
153
+** setting.
154
+**
155
+** A zTag of "*" matches anything.
156
+*/
157
+static int robot_restrict_has_tag(const char *zTag){
158
+ static const char *zGlob = 0;
159
+ if( zGlob==0 ){
160
+ zGlob = db_get("robot-restrict",robot_restrict_default());
161
+ if( zGlob==0 ) zGlob = "";
162
+ }
163
+ if( zGlob[0]==0 || fossil_strcmp(zGlob, "off")==0 ){
164
+ return 0;
165
+ }
166
+ if( zTag==0 || (zTag[0]=='*' && zTag[1]==0) ){
167
+ return 1;
168
+ }
169
+ return glob_multi_match(zGlob,zTag);
170
+}
171
+
172
+/*
173
+** Check the request URI to see if it matches one of the URI
174
+** exceptions listed in the robot-exception setting. Return true
175
+** if it does. Return false if it does not.
176
+**
177
+** For the purposes of this routine, the "request URI" means
178
+** the REQUEST_URI value with the SCRIPT_NAME prefix removed and
179
+** with QUERY_STRING appended with a "?" separator if QUERY_STRING
180
+** is not empty.
181
+**
182
+** If the robot-exception setting does not exist or is an empty
183
+** string, then return false.
184
+*/
185
+int robot_exception(void){
186
+ const char *zRE = db_get("robot-exception",0);
187
+ const char *zQS; /* QUERY_STRING */
188
+ const char *zURI; /* REQUEST_URI */
189
+ const char *zSN; /* SCRIPT_NAME */
190
+ const char *zNL; /* Next newline character */
191
+ char *zRequest; /* REQUEST_URL w/o SCRIPT_NAME prefix + QUERY_STRING */
192
+ int nRequest; /* Length of zRequest in bytes */
193
+ size_t nURI, nSN; /* Length of zURI and zSN */
194
+ int bMatch = 0; /* True if there is a match */
195
+
196
+ if( zRE==0 ) return 0;
197
+ if( zRE[0]==0 ) return 0;
198
+ zURI = PD("REQUEST_URI","");
199
+ nURI = strlen(zURI);
200
+ zSN = PD("SCRIPT_NAME","");
201
+ nSN = strlen(zSN);
202
+ if( nSN<=nURI ) zURI += nSN;
203
+ zQS = P("QUERY_STRING");
204
+ if( zQS && zQS[0] ){
205
+ zRequest = mprintf("%s?%s", zURI, zQS);
206
+ }else{
207
+ zRequest = fossil_strdup(zURI);
208
+ }
209
+ nRequest = (int)strlen(zRequest);
210
+ while( zRE[0] && bMatch==0 ){
211
+ char *z;
212
+ const char *zErr;
213
+ size_t n;
214
+ ReCompiled *pRe;
215
+ zNL = strchr(zRE,'\n');
216
+ if( zNL ){
217
+ n = (size_t)(zNL - zRE)+1;
218
+ while( zNL>zRE && fossil_isspace(zNL[0]) ) zNL--;
219
+ if( zNL==zRE ){
220
+ zRE += n;
221
+ continue;
222
+ }
223
+ }else{
224
+ n = strlen(zRE);
225
+ }
226
+ z = mprintf("%.*s", (int)(zNL - zRE)+1, zRE);
227
+ zRE += n;
228
+ zErr = fossil_re_compile(&pRe, z, 0);
229
+ if( zErr ){
230
+ fossil_warning("robot-exception error \"%s\" in expression \"%s\"\n",
231
+ zErr, z);
232
+ fossil_free(z);
233
+ continue;
234
+ }
235
+ fossil_free(z);
236
+ bMatch = re_match(pRe, (const unsigned char*)zRequest, nRequest);
237
+ re_free(pRe);
238
+ }
239
+ fossil_free(zRequest);
240
+ return bMatch;
241
+}
242
+
243
+/*
244
+** Return true if one or more of the conditions below are true.
245
+** Return false if all of the following are false:
246
+**
247
+** * The zTag is on the robot-restrict list
248
+**
249
+** * The client that submitted the HTTP request might be
250
+** a robot
251
+**
252
+** * The Request URI does not match any of the exceptions
253
+** in the robot-exception setting.
254
+**
255
+** In other words, return true if a call to robot_restrict() would
256
+** return true and false if a call to robot_restrict() would return
257
+** false.
258
+**
259
+** The difference between this routine an robot_restrict() is that
260
+** this routine does not generate a proof-of-work captcha. This
261
+** routine does not change the HTTP reply in any way. It simply
262
+** returns true or false.
263
+*/
264
+int robot_would_be_restricted(const char *zTag){
265
+ if( robot.resultCache==KNOWN_NOT_ROBOT ) return 0;
266
+ if( !robot_restrict_has_tag(zTag) ) return 0;
267
+ if( !client_might_be_a_robot() ) return 0;
268
+ if( robot_exception() ){
269
+ robot.resultCache = KNOWN_NOT_ROBOT;
270
+ return 0;
271
+ }
272
+ return 1;
273
+}
274
+
275
+/*
276
+** Check to see if the page named in the argument is on the
277
+** robot-restrict list. If it is on the list and if the user
278
+** is might be a robot, then bring up a captcha to test to make
279
+** sure that client is not a robot.
280
+**
281
+** This routine returns true if a captcha was rendered and if subsequent
282
+** page generation should be aborted. It returns false if the page
283
+** should not be restricted and should be rendered normally.
284
+*/
285
+int robot_restrict(const char *zTag){
286
+ if( robot_would_be_restricted(zTag) ){
287
+ /* Generate the proof-of-work captcha */
288
+ ask_for_proof_that_client_is_not_robot();
289
+ return 1;
290
+ }else{
291
+ return 0;
292
+ }
293
+}
294
+
295
+/*
296
+** Check to see if a robot is allowed to download a tarball, ZIP archive,
297
+** or SQL Archive for a particular check-in identified by the "rid"
298
+** argument. Return true to block the download. Return false to
299
+** continue. Prior to returning true, a captcha is presented to the user.
300
+** No output is generated when returning false.
301
+**
302
+** The rules:
303
+**
304
+** (1) If "zipX" is missing from the robot-restrict setting, then robots
305
+** are allowed to download any archive. None of the remaining rules
306
+** below are consulted unless "zipX" is on the robot-restrict setting.
307
+**
308
+** (2) If the robot-zip-leaf setting is true, then robots are allowed
309
+** to download archives for any leaf check-in. This allows URL like
310
+** /tarball/trunk/archive.tar.gz to work since branch labels like "trunk"
311
+** always resolve to a leaf.
312
+**
313
+** (3) If the robot-zip-tag setting is a comma-separated tags, then any
314
+** check-in that contains one of the tags on that list is allowed to
315
+** be downloaded. This allows check-ins with tags like "release" or
316
+** "robot-access" to be downloaded by robots.
317
+*/
318
+int robot_restrict_zip(int rid){
319
+ const char *zTag;
320
+ if( !robot_restrict_has_tag("zipX") || !client_might_be_a_robot() ){
321
+ return 0; /* Rule (1) */
322
+ }
323
+
324
+ if( db_get_boolean("robot-zip-leaf",0) && is_a_leaf(rid) ){
325
+ return 0; /* Rule (2) */
326
+ }
327
+
328
+ zTag = db_get("robot-zip-tag",0);
329
+ if( zTag && zTag[0] && fossil_strcmp(zTag,"off")!=0 ){
330
+ int ok = 0;
331
+ Stmt q;
332
+ db_prepare(&q,
333
+ "SELECT substr(tagname,5) FROM tagxref, tag"
334
+ " WHERE tagxref.rid=%d"
335
+ " AND tag.tagid=tagxref.tagid"
336
+ " AND tagxref.tagtype=1"
337
+ " AND tag.tagname GLOB 'sym-*'",
338
+ rid
339
+ );
340
+ while( !ok && db_step(&q)==SQLITE_ROW ){
341
+ if( glob_multi_match(zTag, db_column_text(&q,0)) ) ok = 1;
342
+ }
343
+ db_finalize(&q);
344
+ if( ok ) return 0; /* Rule (3) */
345
+ }
346
+
347
+ /* Generate the proof-of-work captcha */
348
+ ask_for_proof_that_client_is_not_robot();
349
+ return 1;
350
+}
351
+
352
+/*
353
+** WEBPAGE: test-robotck
354
+**
355
+** Run the robot_restrict() function using the value of the "name="
356
+** query parameter as an argument. Used for testing the robot_restrict()
357
+** logic.
358
+**
359
+** Whenever this page is successfully rendered (when it doesn't go to
360
+** the captcha) it deletes the proof-of-work cookie. So reloading the
361
+** page will reset the cookie and restart the verification.
362
+**
363
+** If the zip=CHECKIN query parameter is provided, then also invoke
364
+** robot_restrict_archive() on the RID of CHECKIN.
365
+*/
366
+void robot_restrict_test_page(void){
367
+ const char *zName = P("name");
368
+ const char *zZip = P("zip");
369
+ const char *zP1 = P("proof");
370
+ const char *zP2 = P(ROBOT_COOKIE);
371
+ const char *z;
372
+ int rid = 0;
373
+ if( zName==0 || zName[0]==0 ) zName = g.zPath;
374
+ login_check_credentials();
375
+ if( g.zLogin==0 ){ login_needed(1); return; }
376
+ g.zLogin = 0;
377
+ if( robot_restrict(zName) ) return;
378
+ if( zZip && zZip[0] ){
379
+ rid = symbolic_name_to_rid(zZip, "ci");
380
+ if( rid && robot_restrict_zip(rid) ) return;
381
+ }
382
+ style_set_current_feature("test");
383
+ style_header("robot_restrict() test");
384
+ @ <h1>Captcha passed</h1>
385
+ @
386
+ @ <p>
387
+ if( zP1 && zP1[0] ){
388
+ @ proof=%h(zP1)<br>
389
+ }
390
+ if( zP2 && zP2[0] ){
391
+ @ %h(ROBOT_COOKIE)=%h(zP2)<br>
392
+ cgi_set_cookie(ROBOT_COOKIE,"",0,-1);
393
+ }
394
+ if( zZip && zZip[0] ){
395
+ @ zip=%h(zZip)<br>
396
+ @ rid=%d(rid)<br>
397
+ }
398
+ if( g.perm.Admin ){
399
+ z = db_get("robot-restrict",robot_restrict_default());
400
+ if( z && z[0] ){
401
+ @ robot-restrict=%h(z)</br>
402
+ }
403
+ @ robot.h1=%u(robot.h1)<br>
404
+ @ robot.h2=%u(robot.h2)<br>
405
+ switch( robot.resultCache ){
406
+ case MIGHT_BE_ROBOT: {
407
+ @ robot.resultCache=MIGHT_BE_ROBOT<br>
408
+ break;
409
+ }
410
+ ca
--- a/src/robot.c
+++ b/src/robot.c
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/robot.c
+++ b/src/robot.c
@@ -0,0 +1,410 @@
1 t to treat as human. Allow it through and also set the robot cookie.
2 */
3 z = P("token");
4 if( z!=0 ){
5 if( db_exists("SELECT 1 FROM config"
6 " WHERE name='token-%q'"
7 " AND json_valid(value,6)"
8 " AND value->>'user' IS NOT NULL", z)
9 ){
10 char *zVal;
11 robot_pow_hash();
12 zVal = mprintf("%u", robot.h1);
13 cgi_set_cookie(ROBOT_COOKIE,zVal,"/",900);
14 fossil_free(zVal);
15 robot.resultCache = KNOWN_NOT_ROBOT;
16 return 0; /* There is a valid token= query parameter */
17 }
18 cgi_tag_query_parameter("token");
19 }
20
21 /* We have no proof that the request is coming from an interactive
22 ** human session, so assume the request comes from a robot.
23 */
24 robot.resultCache = MIGHT_BE_ROBOT;
25 return 1;
26 }
27
28 /*
29 ** Rewrite the current page with content that attempts
30 ** to prove that the client is not a robot.
31 */
32 static void ask_for_proof_that_client_is_not_robot(void){
33 unsigned p1, p2, p3, p4, p5, k2, k3;
34 int k;
35
36 /* Ask the client to present proof-of-work */
37 cgi_reset_content();
38 cgi_set_content_type("text/html");
39 style_header("Browser Verification");
40 @ <h1 id="x1">Checking to see if you are a robot<span id="x2"></span></h1>
41 @ <form method="GET" id="x6"><p>
42 @ <span id="x3" style="visibility:hidden;">\
43 @ Press <input type="submit" id="x5" value="Ok" focus> to continue</span>
44 @ <span id="x7" style="visibility:hidden;">You appear to be a robot.</span>\
45 @ </p>
46 if( g.zExtra && g.zExtra[0] ) cgi_tag_query_parameter("name");
47 cgi_query_parameters_to_hidden();
48 @ <input id="x4" type="hidden" name="proof" value="0">
49 @ </form>
50 @ <script nonce='%s(style_nonce())'>
51 @ function aaa(x){return document.getElementById(x);The "diff" tagge name. The "diff" tag
52 *
53 ** /vpatch. Thefdiff, and /vpatch. The
54 **
55 ** also coversaise. "zip" also covers
56 ** /tarba
57 ** then itaracter appended then it
58 ** only
59 ** particularly difficult to compute. In all other case, the tag should
60 ***
61 ** Usually the tag should exaUseful "X" t
62 ** "zipX". See to disable all robot restrictions.
63 */
64 /*
65 ** SETTING: robot-exception width=40 block-text
66 **
67 ** The value of this setting should be a regular expression.
68 ** If it matches the REQUEST_URI without the SCRIPT_NAME prefix
69 ** matches this regular expression, then the request is an exception
70 ** to anti-robot defenses and should be allowed through. For
71 ** example, to allow robots to download tarballs or ZIP archives
72 ** for named versions and releases, you could use an expression like
73 ** this:
74 **
75 ** ^/tarball/(version-[0-9.]+|release)/
76 **
77 ** This setting can hold multiple regular expressions, one
78 ** regular expression per line. The input URL is exempted from
79 ** anti-robot defenses if any of the multiple regular expressions
80 ** matches.
81 */
82 /*
83 ** SETTING: robot-zip-leaf boolean
84 **
85 ** If this setting is true, the robots are allowed to download tarballs,
86 ** ZIP-archives, and SQL-archives even though "zipX" is found in
87 ** the [[robot-restrict]] setting as long as the specific check-in being
88 ** downloaded is a leaf check-in.
89 */
90 /*
91 ** SETTING: robot-zip-tag width=40 block-text
92 **
93 ** If this setting is a list of GLOB patterns matching tags,
94 ** then robots are allowed to download tarballs, ZIP-archives, and
95 ** SQL-archives even though "zipX" appears in [[robot-restrict]], as long as
96 ** the specific check-in being downloaded has a tags that matches
97 ** the GLOB list of this setting. Recommended value:
98 ** "release,robot-access".
99 */
100
101 /*
102 ** Return the default restriction GLOB
103 */
104 const char *robot_restrict_default(void){
105 /* NOTE: The default value is also mentioned in the online help screen of
106 ** the "robot-restrict" setting, and in the www/antibot.wiki document. */
107 return "timelineX,diff,annotate,fileage,file,finfo,reports,"
108 "tree,hexdump,download";
109 }
110
111 /*
112 ** Return true if zTag matches one of the tags in the rT_URI without the SCRIPT_NAME prefix
113 ** matches this regular expression, then the request is an exception
114 ** to anti-robot defenses and should be allowed through. For
115 ** example, to allow robots to download tarballs or ZIP archives
116 ** for named versions and releases, you could]+|release)/
117 **
118 ** This setting can hold multiple regular expressions, one
119 ** regular expression per line. The input URL is exempted from
120 ** anti-robot defenses if any of the multiple regular expressions
121 ** matches.
122 */
123 /*
124 ** SETTING: robot-zip-leaf boolean
125 **
126 ** If this setting is true, the robots are allowed to download tarballs,
127 ** ZIP-archives, and SQL-archives even though "zipX" is found in
128 ** the [[robot-restrict]] setting as long as the specific check-in being
129 ** downloaded is a leaf check-in.
130 */
131 /*
132 ** SETTING: robot-zip-tag width=40 block-text
133 **
134 ** If this setting is a list of GLOB patterns matching tags,
135 ** then robots are allowed to download tarballs, ZIP-archives, and
136 ** SQL-archives even though "zipX" appears in [[ro downloaded has a tags that matches
137 ** the GLOB list of this setting. Recommended value:
138 ** "release,robot-access".
139 */
140
141 /*
142 ** Return the default restriction GLOB
143 */
144 const char *robot_restrict_default(void){
145 /* NOTE: The default value is also mentioned in the online help screen of
146 ** the "robot-restrict" setting, and in the www/antibot.wiki document. */
147 return "timelineX,diff,annotate,fileage,file,finfo,reports,"
148 "tree,hexdump,download";
149 }
150
151 /*
152 ** Return true if zTag matches one of the tags in the robot-restrict
153 ** setting.
154 **
155 ** A zTag of "*" matches anything.
156 */
157 static int robot_restrict_has_tag(const char *zTag){
158 static const char *zGlob = 0;
159 if( zGlob==0 ){
160 zGlob = db_get("robot-restrict",robot_restrict_default());
161 if( zGlob==0 ) zGlob = "";
162 }
163 if( zGlob[0]==0 || fossil_strcmp(zGlob, "off")==0 ){
164 return 0;
165 }
166 if( zTag==0 || (zTag[0]=='*' && zTag[1]==0) ){
167 return 1;
168 }
169 return glob_multi_match(zGlob,zTag);
170 }
171
172 /*
173 ** Check the request URI to see if it matches one of the URI
174 ** exceptions listed in the robot-exception setting. Return true
175 ** if it does. Return false if it does not.
176 **
177 ** For the purposes of this routine, the "request URI" means
178 ** the REQUEST_URI value with the SCRIPT_NAME prefix removed and
179 ** with QUERY_STRING appended with a "?" separator if QUERY_STRING
180 ** is not empty.
181 **
182 ** If the robot-exception setting does not exist or is an empty
183 ** string, then return false.
184 */
185 int robot_exception(void){
186 const char *zRE = db_get("robot-exception",0);
187 const char *zQS; /* QUERY_STRING */
188 const char *zURI; /* REQUEST_URI */
189 const char *zSN; /* SCRIPT_NAME */
190 const char *zNL; /* Next newline character */
191 char *zRequest; /* REQUEST_URL w/o SCRIPT_NAME prefix + QUERY_STRING */
192 int nRequest; /* Length of zRequest in bytes */
193 size_t nURI, nSN; /* Length of zURI and zSN */
194 int bMatch = 0; /* True if there is a match */
195
196 if( zRE==0 ) return 0;
197 if( zRE[0]==0 ) return 0;
198 zURI = PD("REQUEST_URI","");
199 nURI = strlen(zURI);
200 zSN = PD("SCRIPT_NAME","");
201 nSN = strlen(zSN);
202 if( nSN<=nURI ) zURI += nSN;
203 zQS = P("QUERY_STRING");
204 if( zQS && zQS[0] ){
205 zRequest = mprintf("%s?%s", zURI, zQS);
206 }else{
207 zRequest = fossil_strdup(zURI);
208 }
209 nRequest = (int)strlen(zRequest);
210 while( zRE[0] && bMatch==0 ){
211 char *z;
212 const char *zErr;
213 size_t n;
214 ReCompiled *pRe;
215 zNL = strchr(zRE,'\n');
216 if( zNL ){
217 n = (size_t)(zNL - zRE)+1;
218 while( zNL>zRE && fossil_isspace(zNL[0]) ) zNL--;
219 if( zNL==zRE ){
220 zRE += n;
221 continue;
222 }
223 }else{
224 n = strlen(zRE);
225 }
226 z = mprintf("%.*s", (int)(zNL - zRE)+1, zRE);
227 zRE += n;
228 zErr = fossil_re_compile(&pRe, z, 0);
229 if( zErr ){
230 fossil_warning("robot-exception error \"%s\" in expression \"%s\"\n",
231 zErr, z);
232 fossil_free(z);
233 continue;
234 }
235 fossil_free(z);
236 bMatch = re_match(pRe, (const unsigned char*)zRequest, nRequest);
237 re_free(pRe);
238 }
239 fossil_free(zRequest);
240 return bMatch;
241 }
242
243 /*
244 ** Return true if one or more of the conditions below are true.
245 ** Return false if all of the following are false:
246 **
247 ** * The zTag is on the robot-restrict list
248 **
249 ** * The client that submitted the HTTP request might be
250 ** a robot
251 **
252 ** * The Request URI does not match any of the exceptions
253 ** in the robot-exception setting.
254 **
255 ** In other words, return true if a call to robot_restrict() would
256 ** return true and false if a call to robot_restrict() would return
257 ** false.
258 **
259 ** The difference between this routine an robot_restrict() is that
260 ** this routine does not generate a proof-of-work captcha. This
261 ** routine does not change the HTTP reply in any way. It simply
262 ** returns true or false.
263 */
264 int robot_would_be_restricted(const char *zTag){
265 if( robot.resultCache==KNOWN_NOT_ROBOT ) return 0;
266 if( !robot_restrict_has_tag(zTag) ) return 0;
267 if( !client_might_be_a_robot() ) return 0;
268 if( robot_exception() ){
269 robot.resultCache = KNOWN_NOT_ROBOT;
270 return 0;
271 }
272 return 1;
273 }
274
275 /*
276 ** Check to see if the page named in the argument is on the
277 ** robot-restrict list. If it is on the list and if the user
278 ** is might be a robot, then bring up a captcha to test to make
279 ** sure that client is not a robot.
280 **
281 ** This routine returns true if a captcha was rendered and if subsequent
282 ** page generation should be aborted. It returns false if the page
283 ** should not be restricted and should be rendered normally.
284 */
285 int robot_restrict(const char *zTag){
286 if( robot_would_be_restricted(zTag) ){
287 /* Generate the proof-of-work captcha */
288 ask_for_proof_that_client_is_not_robot();
289 return 1;
290 }else{
291 return 0;
292 }
293 }
294
295 /*
296 ** Check to see if a robot is allowed to download a tarball, ZIP archive,
297 ** or SQL Archive for a particular check-in identified by the "rid"
298 ** argument. Return true to block the download. Return false to
299 ** continue. Prior to returning true, a captcha is presented to the user.
300 ** No output is generated when returning false.
301 **
302 ** The rules:
303 **
304 ** (1) If "zipX" is missing from the robot-restrict setting, then robots
305 ** are allowed to download any archive. None of the remaining rules
306 ** below are consulted unless "zipX" is on the robot-restrict setting.
307 **
308 ** (2) If the robot-zip-leaf setting is true, then robots are allowed
309 ** to download archives for any leaf check-in. This allows URL like
310 ** /tarball/trunk/archive.tar.gz to work since branch labels like "trunk"
311 ** always resolve to a leaf.
312 **
313 ** (3) If the robot-zip-tag setting is a comma-separated tags, then any
314 ** check-in that contains one of the tags on that list is allowed to
315 ** be downloaded. This allows check-ins with tags like "release" or
316 ** "robot-access" to be downloaded by robots.
317 */
318 int robot_restrict_zip(int rid){
319 const char *zTag;
320 if( !robot_restrict_has_tag("zipX") || !client_might_be_a_robot() ){
321 return 0; /* Rule (1) */
322 }
323
324 if( db_get_boolean("robot-zip-leaf",0) && is_a_leaf(rid) ){
325 return 0; /* Rule (2) */
326 }
327
328 zTag = db_get("robot-zip-tag",0);
329 if( zTag && zTag[0] && fossil_strcmp(zTag,"off")!=0 ){
330 int ok = 0;
331 Stmt q;
332 db_prepare(&q,
333 "SELECT substr(tagname,5) FROM tagxref, tag"
334 " WHERE tagxref.rid=%d"
335 " AND tag.tagid=tagxref.tagid"
336 " AND tagxref.tagtype=1"
337 " AND tag.tagname GLOB 'sym-*'",
338 rid
339 );
340 while( !ok && db_step(&q)==SQLITE_ROW ){
341 if( glob_multi_match(zTag, db_column_text(&q,0)) ) ok = 1;
342 }
343 db_finalize(&q);
344 if( ok ) return 0; /* Rule (3) */
345 }
346
347 /* Generate the proof-of-work captcha */
348 ask_for_proof_that_client_is_not_robot();
349 return 1;
350 }
351
352 /*
353 ** WEBPAGE: test-robotck
354 **
355 ** Run the robot_restrict() function using the value of the "name="
356 ** query parameter as an argument. Used for testing the robot_restrict()
357 ** logic.
358 **
359 ** Whenever this page is successfully rendered (when it doesn't go to
360 ** the captcha) it deletes the proof-of-work cookie. So reloading the
361 ** page will reset the cookie and restart the verification.
362 **
363 ** If the zip=CHECKIN query parameter is provided, then also invoke
364 ** robot_restrict_archive() on the RID of CHECKIN.
365 */
366 void robot_restrict_test_page(void){
367 const char *zName = P("name");
368 const char *zZip = P("zip");
369 const char *zP1 = P("proof");
370 const char *zP2 = P(ROBOT_COOKIE);
371 const char *z;
372 int rid = 0;
373 if( zName==0 || zName[0]==0 ) zName = g.zPath;
374 login_check_credentials();
375 if( g.zLogin==0 ){ login_needed(1); return; }
376 g.zLogin = 0;
377 if( robot_restrict(zName) ) return;
378 if( zZip && zZip[0] ){
379 rid = symbolic_name_to_rid(zZip, "ci");
380 if( rid && robot_restrict_zip(rid) ) return;
381 }
382 style_set_current_feature("test");
383 style_header("robot_restrict() test");
384 @ <h1>Captcha passed</h1>
385 @
386 @ <p>
387 if( zP1 && zP1[0] ){
388 @ proof=%h(zP1)<br>
389 }
390 if( zP2 && zP2[0] ){
391 @ %h(ROBOT_COOKIE)=%h(zP2)<br>
392 cgi_set_cookie(ROBOT_COOKIE,"",0,-1);
393 }
394 if( zZip && zZip[0] ){
395 @ zip=%h(zZip)<br>
396 @ rid=%d(rid)<br>
397 }
398 if( g.perm.Admin ){
399 z = db_get("robot-restrict",robot_restrict_default());
400 if( z && z[0] ){
401 @ robot-restrict=%h(z)</br>
402 }
403 @ robot.h1=%u(robot.h1)<br>
404 @ robot.h2=%u(robot.h2)<br>
405 switch( robot.resultCache ){
406 case MIGHT_BE_ROBOT: {
407 @ robot.resultCache=MIGHT_BE_ROBOT<br>
408 break;
409 }
410 ca
+6 -6
--- src/search.c
+++ src/search.c
@@ -130,14 +130,14 @@
130130
search_end(p);
131131
}else{
132132
p = fossil_malloc(sizeof(*p));
133133
memset(p, 0, sizeof(*p));
134134
}
135
- p->zPattern = z = fossil_strdup(zPattern);
136
- p->zMarkBegin = fossil_strdup(zMarkBegin);
137
- p->zMarkEnd = fossil_strdup(zMarkEnd);
138
- p->zMarkGap = fossil_strdup(zMarkGap);
135
+ p->zPattern = z = mprintf("%s",zPattern);
136
+ p->zMarkBegin = mprintf("%s",zMarkBegin);
137
+ p->zMarkEnd = mprintf("%s",zMarkEnd);
138
+ p->zMarkGap = mprintf("%s",zMarkGap);
139139
p->fSrchFlg = fSrchFlg;
140140
blob_init(&p->snip, 0, 0);
141141
while( *z && p->nTerm<SEARCH_MAX_TERM ){
142142
while( *z && !ISALNUM(*z) ){ z++; }
143143
if( *z==0 ) break;
@@ -983,11 +983,11 @@
983983
zPrefix = "Built-in help for the";
984984
}
985985
db_multi_exec(
986986
"INSERT INTO x(label,url,score,id,snip)"
987987
" SELECT format('%q \"%%s\" %%s',name,type),"
988
- " '/help?cmd='||name,"
988
+ " '/help/'||name,"
989989
" search_score(),"
990990
" 'h'||rowid,"
991991
" search_snippet()"
992992
" FROM helptext"
993993
" WHERE search_match(format('the \"%%s\" %%s',name,type),"
@@ -1076,11 +1076,11 @@
10761076
** causing errors in FTS5 searches with inputs which contain AND, OR,
10771077
** and symbols like #. The caller is responsible for passing the
10781078
** result to fossil_free().
10791079
*/
10801080
char *search_simplify_pattern(const char * zPattern){
1081
- char *zPat = fossil_strdup(zPattern);
1081
+ char *zPat = mprintf("%s",zPattern);
10821082
int i;
10831083
for(i=0; zPat[i]; i++){
10841084
if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
10851085
if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
10861086
}
10871087
--- src/search.c
+++ src/search.c
@@ -130,14 +130,14 @@
130 search_end(p);
131 }else{
132 p = fossil_malloc(sizeof(*p));
133 memset(p, 0, sizeof(*p));
134 }
135 p->zPattern = z = fossil_strdup(zPattern);
136 p->zMarkBegin = fossil_strdup(zMarkBegin);
137 p->zMarkEnd = fossil_strdup(zMarkEnd);
138 p->zMarkGap = fossil_strdup(zMarkGap);
139 p->fSrchFlg = fSrchFlg;
140 blob_init(&p->snip, 0, 0);
141 while( *z && p->nTerm<SEARCH_MAX_TERM ){
142 while( *z && !ISALNUM(*z) ){ z++; }
143 if( *z==0 ) break;
@@ -983,11 +983,11 @@
983 zPrefix = "Built-in help for the";
984 }
985 db_multi_exec(
986 "INSERT INTO x(label,url,score,id,snip)"
987 " SELECT format('%q \"%%s\" %%s',name,type),"
988 " '/help?cmd='||name,"
989 " search_score(),"
990 " 'h'||rowid,"
991 " search_snippet()"
992 " FROM helptext"
993 " WHERE search_match(format('the \"%%s\" %%s',name,type),"
@@ -1076,11 +1076,11 @@
1076 ** causing errors in FTS5 searches with inputs which contain AND, OR,
1077 ** and symbols like #. The caller is responsible for passing the
1078 ** result to fossil_free().
1079 */
1080 char *search_simplify_pattern(const char * zPattern){
1081 char *zPat = fossil_strdup(zPattern);
1082 int i;
1083 for(i=0; zPat[i]; i++){
1084 if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
1085 if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
1086 }
1087
--- src/search.c
+++ src/search.c
@@ -130,14 +130,14 @@
130 search_end(p);
131 }else{
132 p = fossil_malloc(sizeof(*p));
133 memset(p, 0, sizeof(*p));
134 }
135 p->zPattern = z = mprintf("%s",zPattern);
136 p->zMarkBegin = mprintf("%s",zMarkBegin);
137 p->zMarkEnd = mprintf("%s",zMarkEnd);
138 p->zMarkGap = mprintf("%s",zMarkGap);
139 p->fSrchFlg = fSrchFlg;
140 blob_init(&p->snip, 0, 0);
141 while( *z && p->nTerm<SEARCH_MAX_TERM ){
142 while( *z && !ISALNUM(*z) ){ z++; }
143 if( *z==0 ) break;
@@ -983,11 +983,11 @@
983 zPrefix = "Built-in help for the";
984 }
985 db_multi_exec(
986 "INSERT INTO x(label,url,score,id,snip)"
987 " SELECT format('%q \"%%s\" %%s',name,type),"
988 " '/help/'||name,"
989 " search_score(),"
990 " 'h'||rowid,"
991 " search_snippet()"
992 " FROM helptext"
993 " WHERE search_match(format('the \"%%s\" %%s',name,type),"
@@ -1076,11 +1076,11 @@
1076 ** causing errors in FTS5 searches with inputs which contain AND, OR,
1077 ** and symbols like #. The caller is responsible for passing the
1078 ** result to fossil_free().
1079 */
1080 char *search_simplify_pattern(const char * zPattern){
1081 char *zPat = mprintf("%s",zPattern);
1082 int i;
1083 for(i=0; zPat[i]; i++){
1084 if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
1085 if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
1086 }
1087
--- src/security_audit.c
+++ src/security_audit.c
@@ -369,11 +369,11 @@
369369
zVulnReport = db_get("vuln-report","log");
370370
if( fossil_strcmp(zVulnReport,"block")!=0
371371
&& fossil_strcmp(zVulnReport,"fatal")!=0
372372
){
373373
@ <li><p><b>WARNING:</b>
374
- @ The <a href="%R/help?cmd=vuln-report">vuln-report setting</a>
374
+ @ The <a href="%R/help/vuln-report">vuln-report setting</a>
375375
@ has a value of "%h(zVulnReport)". This disables defenses against
376376
@ XSS or SQL-injection vulnerabilities caused by coding errors in
377377
@ custom TH1 scripts. For the best security, change
378378
@ the value of the vuln-report setting to "block" or "fatal".
379379
}
@@ -954,11 +954,13 @@
954954
if( prevWasTime ){
955955
if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
956956
bOutput = (eType & 0x01)!=0;
957957
nHack++;
958958
}else
959
- if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){
959
+ if( (strncmp(z,"panic: ", 7)==0 && strncmp(z+7,"Timeout",7)!=0)
960
+ || strstr(z," assertion fault ")!=0
961
+ ){
960962
bOutput = (eType & 0x02)!=0;
961963
nPanic++;
962964
}else
963965
if( strncmp(z,"SMTP:", 5)==0 ){
964966
bOutput = (eType & 0x20)!=0;
965967
--- src/security_audit.c
+++ src/security_audit.c
@@ -369,11 +369,11 @@
369 zVulnReport = db_get("vuln-report","log");
370 if( fossil_strcmp(zVulnReport,"block")!=0
371 && fossil_strcmp(zVulnReport,"fatal")!=0
372 ){
373 @ <li><p><b>WARNING:</b>
374 @ The <a href="%R/help?cmd=vuln-report">vuln-report setting</a>
375 @ has a value of "%h(zVulnReport)". This disables defenses against
376 @ XSS or SQL-injection vulnerabilities caused by coding errors in
377 @ custom TH1 scripts. For the best security, change
378 @ the value of the vuln-report setting to "block" or "fatal".
379 }
@@ -954,11 +954,13 @@
954 if( prevWasTime ){
955 if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
956 bOutput = (eType & 0x01)!=0;
957 nHack++;
958 }else
959 if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){
 
 
960 bOutput = (eType & 0x02)!=0;
961 nPanic++;
962 }else
963 if( strncmp(z,"SMTP:", 5)==0 ){
964 bOutput = (eType & 0x20)!=0;
965
--- src/security_audit.c
+++ src/security_audit.c
@@ -369,11 +369,11 @@
369 zVulnReport = db_get("vuln-report","log");
370 if( fossil_strcmp(zVulnReport,"block")!=0
371 && fossil_strcmp(zVulnReport,"fatal")!=0
372 ){
373 @ <li><p><b>WARNING:</b>
374 @ The <a href="%R/help/vuln-report">vuln-report setting</a>
375 @ has a value of "%h(zVulnReport)". This disables defenses against
376 @ XSS or SQL-injection vulnerabilities caused by coding errors in
377 @ custom TH1 scripts. For the best security, change
378 @ the value of the vuln-report setting to "block" or "fatal".
379 }
@@ -954,11 +954,13 @@
954 if( prevWasTime ){
955 if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
956 bOutput = (eType & 0x01)!=0;
957 nHack++;
958 }else
959 if( (strncmp(z,"panic: ", 7)==0 && strncmp(z+7,"Timeout",7)!=0)
960 || strstr(z," assertion fault ")!=0
961 ){
962 bOutput = (eType & 0x02)!=0;
963 nPanic++;
964 }else
965 if( strncmp(z,"SMTP:", 5)==0 ){
966 bOutput = (eType & 0x20)!=0;
967
+156 -85
--- src/setup.c
+++ src/setup.c
@@ -118,10 +118,12 @@
118118
setup_menu_entry("Settings", "setup_settings",
119119
"Web interface to the \"fossil settings\" command");
120120
}
121121
setup_menu_entry("Timeline", "setup_timeline",
122122
"Timeline display preferences");
123
+ setup_menu_entry("Tarballs and ZIPs", "setup_download",
124
+ "Preferences for auto-generated tarballs and ZIP files");
123125
if( setup_user ){
124126
setup_menu_entry("Login-Group", "setup_login_group",
125127
"Manage single sign-on between this repository and others"
126128
" on the same server");
127129
setup_menu_entry("Tickets", "tktsetup",
@@ -140,12 +142,14 @@
140142
setup_menu_entry("URL Aliases", "waliassetup",
141143
"Configure URL aliases");
142144
if( setup_user ){
143145
setup_menu_entry("Notification", "setup_notification",
144146
"Automatic notifications of changes via outbound email");
147
+#if 0 /* Disabled for now. Does this even work? */
145148
setup_menu_entry("Transfers", "xfersetup",
146149
"Configure the transfer system for this repository");
150
+#endif
147151
}
148152
setup_menu_entry("Skins", "setup_skin_admin",
149153
"Select and/or modify the web interface \"skins\"");
150154
setup_menu_entry("Moderation", "setup_modreq",
151155
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
@@ -421,56 +425,38 @@
421425
};
422426
multiple_choice_attribute(
423427
"Enable hyperlinks base on User-Agent and/or Javascript",
424428
"auto-hyperlink", "autohyperlink", "1",
425429
count(azDefenseOpts)/2, azDefenseOpts);
426
- @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
427
- @ including user "nobody", as long as the User-Agent string in the
428
- @ HTTP header indicates that the request is coming from an actual human
429
- @ being. If this setting is "UserAgent only" (2) then the
430
- @ UserAgent string is the only factor considered. If the value of this
431
- @ setting is "UserAgent And Javascript" (1) then Javascript is added that
432
- @ runs after the page loads and fills in the href= values of &lt;a&gt;
433
- @ elements. In either case, &lt;a&gt; tags are only generated if the
434
- @ UserAgent string indicates that the request is coming from a human and
435
- @ not a robot.
436
- @
437
- @ <p>This setting is designed to give easy access to humans while
438
- @ keeping out robots.
439
- @ You do not normally want a robot to walk your entire repository because
440
- @ if it does, your server will end up computing diffs and annotations for
441
- @ every historical version of every file and creating ZIPs and tarballs of
442
- @ every historical check-in, which can use a lot of CPU and bandwidth
443
- @ even for relatively small projects.</p>
444
- @
445
- @ <p>The "UserAgent and Javascript" value for this setting provides
446
- @ superior protection from robots. However, that setting also prevents
447
- @ the visited/unvisited colors on hyperlinks from displaying correctly
448
- @ on Safari-derived browsers. (Chrome and Firefox work fine.) Since
449
- @ Safari is the underlying rendering engine on all iPhones and iPads,
450
- @ this means that hyperlink visited/unvisited colors will not operate
451
- @ on those platforms when "UserAgent and Javascript" is selected.</p>
452
- @
453
- @ <p>Additional parameters that control the behavior of Javascript:</p>
454
- @ <blockquote>
430
+ @ <br>
455431
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
456432
"auto-hyperlink-delay", "ah-delay", "50", 0);
457433
@ <br>
458434
onoff_attribute("Also require a mouse event before enabling hyperlinks",
459435
"auto-hyperlink-mouseover", "ahmo", 0, 0);
460
- @ </blockquote>
436
+ @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
437
+ @ including user "nobody" if the request appears to be from a human.
438
+ @ Disabling hyperlinks helps prevent robots from walking your site and
439
+ @ soaking up all your CPU and bandwidth.
440
+ @ If this setting is "UserAgent only" (2) then the
441
+ @ UserAgent string is the only factor considered. If the value of this
442
+ @ setting is "UserAgent And Javascript" (1) then Javascript is added that
443
+ @ runs after the page loads and fills in the href= values of &lt;a&gt;
444
+ @ elements. In either case, &lt;a&gt; tags are not generated if the
445
+ @ UserAgent string indicates that the client is a robot.
446
+ @ (Property: "auto-hyperlink")</p>
447
+ @
461448
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
462449
@ and "require a mouse event" should be turned on. These values only come
463450
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
464
- @ Javascript").</p>
451
+ @ Javascript").
452
+ @ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")</p>
465453
@
466454
@ <p>To see if Javascript-base hyperlink enabling mechanism is working,
467
- @ visit the <a href="%R/test-env">/test-env</a> page (from a separate
468
- @ web browser that is not logged in, even as "anonymous") and verify
455
+ @ visit the <a href="%R/test-env">/test-env</a> page from a separate
456
+ @ web browser that is not logged in, even as "anonymous" and verify
469457
@ that the "g.jsHref" value is "1".</p>
470
- @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
471
- @ "auto-hyperlink-mouseover"")</p>
472458
}
473459
474460
/*
475461
** WEBPAGE: setup_robot
476462
**
@@ -488,19 +474,74 @@
488474
@ <p>A Fossil website can have billions of pages in its tree, even for a
489475
@ modest project. Many of those pages (examples: diffs and tarballs)
490476
@ might be expensive to compute. A robot that tries to walk the entire
491477
@ website can present a crippling CPU and bandwidth load.
492478
@
493
- @ <p>The settings on this page are intended to help site administrators
494
- @ defend the site against robots.
479
+ @ <p>The settings on this page are intended to help administrators
480
+ @ defend against abusive robots.
495481
@
496482
@ <form action="%R/setup_robot" method="post"><div>
497483
login_insert_csrf_secret();
498484
@ <input type="submit" name="submit" value="Apply Changes"></p>
499485
@ <hr>
486
+ @ <p><b>Do not allow robots access to these pages.</b><br>
487
+ @ If the page name matches the GLOB pattern of this setting, and the
488
+ @ users is "nobody", and the client has not previously passed a captcha
489
+ @ test to show that it is not a robot, then the page is not displayed.
490
+ @ A captcha test is is rendered instead.
491
+ @ The default value for this setting is:
492
+ @ <p>
493
+ @ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
494
+ @ <p>
495
+ @ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
496
+ @ /vpatch. The "annotate" tag covers /annotate and also /blame and
497
+ @ /praise. The "zip" covers itself and also /tarball and /sqlar.
498
+ @ If a tag has an "X" character appended (ex: "timelineX") then it only
499
+ @ applies if query parameters are such that the page is expensive
500
+ @ and/or unusual. In all other case, the tag should exactly match
501
+ @ the page name.
502
+ @
503
+ @ To disable robot restrictions, change this setting to "off".
504
+ @ (Property: robot-restrict)
505
+ @ <br>
506
+ textarea_attribute("", 2, 80,
507
+ "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
508
+
509
+ @ <p><b>Exception #1</b><br>
510
+ @ If "zipX" appears in the robot-restrict list above, then tarballs,
511
+ @ ZIP-archives, and SQL-archives may be downloaded by robots if
512
+ @ the check-in is a leaf (robot-zip-leaf):<br>
513
+ onoff_attribute("Allow tarballs for leaf check-ins",
514
+ "robot-zip-leaf", "rzleaf", 0, 0);
515
+
516
+ @ <p><b>Exception #2</b><br>
517
+ @ If "zipX" appears in the robot-restrict list above, then tarballs,
518
+ @ ZIP-archives, and SQL-archives may be downloaded by robots if
519
+ @ the check-in has one or more tags that match the following
520
+ @ list of GLOB patterns: (robot-zip-tag)<br>
521
+ textarea_attribute("", 2, 80,
522
+ "robot-zip-tag", "rztag", "", 0);
523
+
524
+ @ <p><b>Exception #3</b><br>
525
+ @ If the request URI matches any of the following
526
+ @ <a href="%R/re_rules">regular expressions</a> (one per line), then the
527
+ @ request is exempt from anti-robot defenses.
528
+ @ The regular expression is matched against the REQUEST_URI with the
529
+ @ SCRIPT_NAME prefix removed, and with QUERY_STRING appended following
530
+ @ a "?" if QUERY_STRING exists. (Property: robot-exception)<br>
531
+ textarea_attribute("", 3, 80,
532
+ "robot-exception", "rbexcept", "", 0);
533
+ @ <hr>
500534
addAutoHyperlinkSettings();
501535
536
+ @ <hr>
537
+ entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
538
+ "anoncookls", "840", 0);
539
+ @ <p>The number of minutes for which an anonymous login cookie is valid.
540
+ @ Set to zero to disable anonymous login.
541
+ @ (property: anon-cookie-lifespan)
542
+
502543
@ <hr>
503544
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
504545
"0.0", 0);
505546
@ <p>Some expensive operations (such as computing tarballs, zip archives,
506547
@ or annotation/blame pages) are prohibited if the load average on the host
@@ -508,37 +549,11 @@
508549
@ computations here. Set this to 0.0 to disable the load average limit.
509550
@ This limit is only enforced on Unix servers. On Linux systems,
510551
@ access to the /proc virtual filesystem is required, which means this limit
511552
@ might not work inside a chroot() jail.
512553
@ (Property: "max-loadavg")</p>
513
-
514
- @ <hr>
515
- @ <p><b>Do not allow robots to make complex requests
516
- @ against the following pages.</b>
517
- @ <p> A "complex request" is an HTTP request that has one or more query
518
- @ parameters. Some robots will spend hours juggling around query parameters
519
- @ or even forging fake query parameters in an effort to discover new
520
- @ behavior or to find an SQL injection opportunity or similar. This can
521
- @ waste hours of CPU time and gigabytes of bandwidth on the server. A
522
- @ suggested value for this setting is:
523
- @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
524
- @ (Property: robot-restrict)
525
- @ <br>
526
- textarea_attribute("", 2, 80,
527
- "robot-restrict", "rbrestrict", "", 0);
528
- @ <br> The following comma-separated GLOB pattern allows for exceptions
529
- @ in the maximum number of query parameters before a request is considered
530
- @ complex. If this GLOB pattern exists and is non-empty and if it
531
- @ matches against the pagename followed by "/" and the number of query
532
- @ parameters, then the request is allowed through. For example, the
533
- @ suggested pattern of "timeline/[012]" allows the /timeline page to
534
- @ pass with up to 2 query parameters besides "name".
535
- @ (Property: robot-restrict-qp)
536
- @ <br>
537
- textarea_attribute("", 2, 80,
538
- "robot-restrict-qp", "rbrestrictqp", "", 0);
539
-
554
+ @
540555
@ <hr>
541556
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
542557
@ </div></form>
543558
db_end_transaction(0);
544559
style_finish_page();
@@ -774,10 +789,17 @@
774789
@ "anonymous" that will automatically fill in the CAPTCHA password.
775790
@ This is less secure than forcing the user to do it manually, but is
776791
@ probably secure enough and it is certainly more convenient for
777792
@ anonymous users. (Property: "auto-captcha")</p>
778793
794
+ @ <hr>
795
+ entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
796
+ "anoncookls", "840", 0);
797
+ @ <p>The number of minutes for which an anonymous login cookie is valid.
798
+ @ Set to zero to disable anonymous logins.
799
+ @ (property: anon-cookie-lifespan)
800
+
779801
@ <hr>
780802
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
781803
@ </div></form>
782804
db_end_transaction(0);
783805
style_finish_page();
@@ -968,10 +990,15 @@
968990
"1", "HH:MM:SS",
969991
"2", "YYYY-MM-DD HH:MM",
970992
"3", "YYMMDD HH:MM",
971993
"4", "(off)"
972994
};
995
+ static const char *const azLeafMark[] = {
996
+ "0", "No",
997
+ "1", "Yes",
998
+ "2", "Yes - with emphasis",
999
+ };
9731000
login_check_credentials();
9741001
if( !g.perm.Admin ){
9751002
login_needed(0);
9761003
return;
9771004
}
@@ -1056,10 +1083,16 @@
10561083
@ in a separate box (using CSS class "timelineDate") whenever the date
10571084
@ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
10581085
@ the complete date and time is shown on every timeline entry using the
10591086
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
10601087
1088
+ @ <hr>
1089
+ multiple_choice_attribute("Leaf Markings", "timeline-mark-leaves",
1090
+ "tml", "1", count(azLeafMark)/2, azLeafMark);
1091
+ @ <p>Should timeline entries for leaf check-ins be identified in the
1092
+ @ detail section. (Property: "timeline-mark-leaves")</p>
1093
+
10611094
@ <hr>
10621095
entry_attribute("Max timeline comment length", 6,
10631096
"timeline-max-comment", "tmc", "0", 0);
10641097
@ <p>The maximum length of a comment to be displayed in a timeline.
10651098
@ "0" there is no length limit.
@@ -1158,11 +1191,11 @@
11581191
continue;
11591192
}
11601193
onoff_attribute("", pSet->name,
11611194
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
11621195
is_truth(pSet->def), hasVersionableValue);
1163
- @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1196
+ @ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
11641197
if( pSet->versionable ){
11651198
@ (v)<br>
11661199
} else {
11671200
@ <br>
11681201
}
@@ -1177,11 +1210,11 @@
11771210
(db_get_versioned(pSet->name, NULL, NULL)!=0);
11781211
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
11791212
continue;
11801213
}
11811214
@ <tr><td>
1182
- @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1215
+ @ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
11831216
if( pSet->versionable ){
11841217
@ (v)
11851218
} else {
11861219
@
11871220
}
@@ -1198,11 +1231,11 @@
11981231
if( pSet->width>0 && pSet->forceTextArea ){
11991232
int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
12001233
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
12011234
continue;
12021235
}
1203
- @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
1236
+ @ <a href='%R/help/%s(pSet->name)'>%s(pSet->name)</a>
12041237
if( pSet->versionable ){
12051238
@ (v)<br>
12061239
} else {
12071240
@ <br>
12081241
}
@@ -1309,28 +1342,10 @@
13091342
@ Omit the trailing "/".
13101343
@ If this repo will not be set up as a persistent server and will not
13111344
@ be sending email alerts, then leave this entry blank.
13121345
@ Suggested value: "%h(g.zBaseURL)"
13131346
@ (Property: "email-url")</p>
1314
- @ <hr>
1315
- entry_attribute("Tarball and ZIP-archive Prefix", 20, "short-project-name",
1316
- "spn", "", 0);
1317
- @ <p>This is used as a prefix on the names of generated tarballs and
1318
- @ ZIP archive. For best results, keep this prefix brief and avoid special
1319
- @ characters such as "/" and "\".
1320
- @ If no tarball prefix is specified, then the full Project Name above is used.
1321
- @ (Property: "short-project-name")
1322
- @ </p>
1323
- @ <hr>
1324
- entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0);
1325
- @ <p>The <a href='%R/download'>/download</a> page is designed to provide
1326
- @ a convenient place for newbies
1327
- @ to download a ZIP archive or a tarball of the project. By default,
1328
- @ the latest trunk check-in is downloaded. Change this tag to something
1329
- @ else (ex: release) to alter the behavior of the /download page.
1330
- @ (Property: "download-tag")
1331
- @ </p>
13321347
@ <hr>
13331348
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
13341349
@ <p>Enter the pathname of the page to display when the "Home" menu
13351350
@ option is selected and when no pathname is
13361351
@ specified in the URL. For example, if you visit the url:</p>
@@ -1408,10 +1423,66 @@
14081423
@ <p>The default value is blank, meaning no added entries.
14091424
@ (Property: sitemap-extra)
14101425
@ <p>
14111426
textarea_attribute("Custom Sitemap Entries", 8, 80,
14121427
"sitemap-extra", "smextra", "", 0);
1428
+ @ <hr>
1429
+ @ <p><input type="submit" name="submit" value="Apply Changes"></p>
1430
+ @ </div></form>
1431
+ db_end_transaction(0);
1432
+ style_finish_page();
1433
+}
1434
+
1435
+/*
1436
+** WEBPAGE: setup_download
1437
+**
1438
+** The "Admin/Download" page. Requires Setup privilege.
1439
+*/
1440
+void setup_download(void){
1441
+ login_check_credentials();
1442
+ if( !g.perm.Setup ){
1443
+ login_needed(0);
1444
+ return;
1445
+ }
1446
+
1447
+ style_set_current_feature("setup");
1448
+ style_header("Tarball and ZIP Downloads");
1449
+ db_begin_transaction();
1450
+ @ <form action="%R/setup_download" method="post"><div>
1451
+ login_insert_csrf_secret();
1452
+ @ <input type="submit" name="submit" value="Apply Changes"></p>
1453
+ @ <hr>
1454
+ entry_attribute("Tarball and ZIP Name Prefix", 20, "short-project-name",
1455
+ "spn", "", 0);
1456
+ @ <p>This is used as a prefix for the names of generated tarballs and
1457
+ @ ZIP archive. Keep this prefix brief and use only lower-case ASCII
1458
+ @ characters, digits, "_", "-" in the name. If this setting is blank,
1459
+ @ then the full <a href='%R/help/project-name'>project-name</a> setting
1460
+ @ is used instead.
1461
+ @ (Property: "short-project-name")
1462
+ @ </p>
1463
+ @ <hr>
1464
+ @ <p><b>Configuration for the <a href="%R/download">/download</a> page.</b>
1465
+ @ <p>The value is a TCL list divided into groups of four tokens:
1466
+ @ <ol>
1467
+ @ <li> Maximum number of matches (COUNT).
1468
+ @ <li> Tag to match using glob (TAG).
1469
+ @ <li> Maximum age of check-ins to match (MAX_AGE).
1470
+ @ <li> Comment to apply to matches (COMMENT).
1471
+ @ </ol>
1472
+ @ Each 4-tuple will match zero or more check-ins. The /download page
1473
+ @ displays the union of matches from all 4-tuples.
1474
+ @ See the <a href="%R/help/suggested-downloads">suggested-downloads</a>
1475
+ @ setting documentation for further detail.
1476
+ @ <p>
1477
+ @ The /download page is omitted from the <a href="%R/sitemap">/sitemap</a>
1478
+ @ if the first token is "0" or "off" or "no". The default value
1479
+ @ for this setting is "off".
1480
+ @ (Property: <a href="%R/help/suggested-downloads">suggested-downloads</a>)
1481
+ @ <p>
1482
+ textarea_attribute("", 4, 80,
1483
+ "suggested-downloads", "sgtrlst", "off", 0);
14131484
@ <hr>
14141485
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
14151486
@ </div></form>
14161487
db_end_transaction(0);
14171488
style_finish_page();
14181489
--- src/setup.c
+++ src/setup.c
@@ -118,10 +118,12 @@
118 setup_menu_entry("Settings", "setup_settings",
119 "Web interface to the \"fossil settings\" command");
120 }
121 setup_menu_entry("Timeline", "setup_timeline",
122 "Timeline display preferences");
 
 
123 if( setup_user ){
124 setup_menu_entry("Login-Group", "setup_login_group",
125 "Manage single sign-on between this repository and others"
126 " on the same server");
127 setup_menu_entry("Tickets", "tktsetup",
@@ -140,12 +142,14 @@
140 setup_menu_entry("URL Aliases", "waliassetup",
141 "Configure URL aliases");
142 if( setup_user ){
143 setup_menu_entry("Notification", "setup_notification",
144 "Automatic notifications of changes via outbound email");
 
145 setup_menu_entry("Transfers", "xfersetup",
146 "Configure the transfer system for this repository");
 
147 }
148 setup_menu_entry("Skins", "setup_skin_admin",
149 "Select and/or modify the web interface \"skins\"");
150 setup_menu_entry("Moderation", "setup_modreq",
151 "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
@@ -421,56 +425,38 @@
421 };
422 multiple_choice_attribute(
423 "Enable hyperlinks base on User-Agent and/or Javascript",
424 "auto-hyperlink", "autohyperlink", "1",
425 count(azDefenseOpts)/2, azDefenseOpts);
426 @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
427 @ including user "nobody", as long as the User-Agent string in the
428 @ HTTP header indicates that the request is coming from an actual human
429 @ being. If this setting is "UserAgent only" (2) then the
430 @ UserAgent string is the only factor considered. If the value of this
431 @ setting is "UserAgent And Javascript" (1) then Javascript is added that
432 @ runs after the page loads and fills in the href= values of &lt;a&gt;
433 @ elements. In either case, &lt;a&gt; tags are only generated if the
434 @ UserAgent string indicates that the request is coming from a human and
435 @ not a robot.
436 @
437 @ <p>This setting is designed to give easy access to humans while
438 @ keeping out robots.
439 @ You do not normally want a robot to walk your entire repository because
440 @ if it does, your server will end up computing diffs and annotations for
441 @ every historical version of every file and creating ZIPs and tarballs of
442 @ every historical check-in, which can use a lot of CPU and bandwidth
443 @ even for relatively small projects.</p>
444 @
445 @ <p>The "UserAgent and Javascript" value for this setting provides
446 @ superior protection from robots. However, that setting also prevents
447 @ the visited/unvisited colors on hyperlinks from displaying correctly
448 @ on Safari-derived browsers. (Chrome and Firefox work fine.) Since
449 @ Safari is the underlying rendering engine on all iPhones and iPads,
450 @ this means that hyperlink visited/unvisited colors will not operate
451 @ on those platforms when "UserAgent and Javascript" is selected.</p>
452 @
453 @ <p>Additional parameters that control the behavior of Javascript:</p>
454 @ <blockquote>
455 entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
456 "auto-hyperlink-delay", "ah-delay", "50", 0);
457 @ <br>
458 onoff_attribute("Also require a mouse event before enabling hyperlinks",
459 "auto-hyperlink-mouseover", "ahmo", 0, 0);
460 @ </blockquote>
 
 
 
 
 
 
 
 
 
 
 
461 @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
462 @ and "require a mouse event" should be turned on. These values only come
463 @ into play when the main auto-hyperlink settings is 2 ("UserAgent and
464 @ Javascript").</p>
 
465 @
466 @ <p>To see if Javascript-base hyperlink enabling mechanism is working,
467 @ visit the <a href="%R/test-env">/test-env</a> page (from a separate
468 @ web browser that is not logged in, even as "anonymous") and verify
469 @ that the "g.jsHref" value is "1".</p>
470 @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
471 @ "auto-hyperlink-mouseover"")</p>
472 }
473
474 /*
475 ** WEBPAGE: setup_robot
476 **
@@ -488,19 +474,74 @@
488 @ <p>A Fossil website can have billions of pages in its tree, even for a
489 @ modest project. Many of those pages (examples: diffs and tarballs)
490 @ might be expensive to compute. A robot that tries to walk the entire
491 @ website can present a crippling CPU and bandwidth load.
492 @
493 @ <p>The settings on this page are intended to help site administrators
494 @ defend the site against robots.
495 @
496 @ <form action="%R/setup_robot" method="post"><div>
497 login_insert_csrf_secret();
498 @ <input type="submit" name="submit" value="Apply Changes"></p>
499 @ <hr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500 addAutoHyperlinkSettings();
501
 
 
 
 
 
 
 
502 @ <hr>
503 entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
504 "0.0", 0);
505 @ <p>Some expensive operations (such as computing tarballs, zip archives,
506 @ or annotation/blame pages) are prohibited if the load average on the host
@@ -508,37 +549,11 @@
508 @ computations here. Set this to 0.0 to disable the load average limit.
509 @ This limit is only enforced on Unix servers. On Linux systems,
510 @ access to the /proc virtual filesystem is required, which means this limit
511 @ might not work inside a chroot() jail.
512 @ (Property: "max-loadavg")</p>
513
514 @ <hr>
515 @ <p><b>Do not allow robots to make complex requests
516 @ against the following pages.</b>
517 @ <p> A "complex request" is an HTTP request that has one or more query
518 @ parameters. Some robots will spend hours juggling around query parameters
519 @ or even forging fake query parameters in an effort to discover new
520 @ behavior or to find an SQL injection opportunity or similar. This can
521 @ waste hours of CPU time and gigabytes of bandwidth on the server. A
522 @ suggested value for this setting is:
523 @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
524 @ (Property: robot-restrict)
525 @ <br>
526 textarea_attribute("", 2, 80,
527 "robot-restrict", "rbrestrict", "", 0);
528 @ <br> The following comma-separated GLOB pattern allows for exceptions
529 @ in the maximum number of query parameters before a request is considered
530 @ complex. If this GLOB pattern exists and is non-empty and if it
531 @ matches against the pagename followed by "/" and the number of query
532 @ parameters, then the request is allowed through. For example, the
533 @ suggested pattern of "timeline/[012]" allows the /timeline page to
534 @ pass with up to 2 query parameters besides "name".
535 @ (Property: robot-restrict-qp)
536 @ <br>
537 textarea_attribute("", 2, 80,
538 "robot-restrict-qp", "rbrestrictqp", "", 0);
539
540 @ <hr>
541 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
542 @ </div></form>
543 db_end_transaction(0);
544 style_finish_page();
@@ -774,10 +789,17 @@
774 @ "anonymous" that will automatically fill in the CAPTCHA password.
775 @ This is less secure than forcing the user to do it manually, but is
776 @ probably secure enough and it is certainly more convenient for
777 @ anonymous users. (Property: "auto-captcha")</p>
778
 
 
 
 
 
 
 
779 @ <hr>
780 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
781 @ </div></form>
782 db_end_transaction(0);
783 style_finish_page();
@@ -968,10 +990,15 @@
968 "1", "HH:MM:SS",
969 "2", "YYYY-MM-DD HH:MM",
970 "3", "YYMMDD HH:MM",
971 "4", "(off)"
972 };
 
 
 
 
 
973 login_check_credentials();
974 if( !g.perm.Admin ){
975 login_needed(0);
976 return;
977 }
@@ -1056,10 +1083,16 @@
1056 @ in a separate box (using CSS class "timelineDate") whenever the date
1057 @ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
1058 @ the complete date and time is shown on every timeline entry using the
1059 @ CSS class "timelineTime". (Property: "timeline-date-format")</p>
1060
 
 
 
 
 
 
1061 @ <hr>
1062 entry_attribute("Max timeline comment length", 6,
1063 "timeline-max-comment", "tmc", "0", 0);
1064 @ <p>The maximum length of a comment to be displayed in a timeline.
1065 @ "0" there is no length limit.
@@ -1158,11 +1191,11 @@
1158 continue;
1159 }
1160 onoff_attribute("", pSet->name,
1161 pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1162 is_truth(pSet->def), hasVersionableValue);
1163 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1164 if( pSet->versionable ){
1165 @ (v)<br>
1166 } else {
1167 @ <br>
1168 }
@@ -1177,11 +1210,11 @@
1177 (db_get_versioned(pSet->name, NULL, NULL)!=0);
1178 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1179 continue;
1180 }
1181 @ <tr><td>
1182 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1183 if( pSet->versionable ){
1184 @ (v)
1185 } else {
1186 @
1187 }
@@ -1198,11 +1231,11 @@
1198 if( pSet->width>0 && pSet->forceTextArea ){
1199 int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
1200 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1201 continue;
1202 }
1203 @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
1204 if( pSet->versionable ){
1205 @ (v)<br>
1206 } else {
1207 @ <br>
1208 }
@@ -1309,28 +1342,10 @@
1309 @ Omit the trailing "/".
1310 @ If this repo will not be set up as a persistent server and will not
1311 @ be sending email alerts, then leave this entry blank.
1312 @ Suggested value: "%h(g.zBaseURL)"
1313 @ (Property: "email-url")</p>
1314 @ <hr>
1315 entry_attribute("Tarball and ZIP-archive Prefix", 20, "short-project-name",
1316 "spn", "", 0);
1317 @ <p>This is used as a prefix on the names of generated tarballs and
1318 @ ZIP archive. For best results, keep this prefix brief and avoid special
1319 @ characters such as "/" and "\".
1320 @ If no tarball prefix is specified, then the full Project Name above is used.
1321 @ (Property: "short-project-name")
1322 @ </p>
1323 @ <hr>
1324 entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0);
1325 @ <p>The <a href='%R/download'>/download</a> page is designed to provide
1326 @ a convenient place for newbies
1327 @ to download a ZIP archive or a tarball of the project. By default,
1328 @ the latest trunk check-in is downloaded. Change this tag to something
1329 @ else (ex: release) to alter the behavior of the /download page.
1330 @ (Property: "download-tag")
1331 @ </p>
1332 @ <hr>
1333 entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
1334 @ <p>Enter the pathname of the page to display when the "Home" menu
1335 @ option is selected and when no pathname is
1336 @ specified in the URL. For example, if you visit the url:</p>
@@ -1408,10 +1423,66 @@
1408 @ <p>The default value is blank, meaning no added entries.
1409 @ (Property: sitemap-extra)
1410 @ <p>
1411 textarea_attribute("Custom Sitemap Entries", 8, 80,
1412 "sitemap-extra", "smextra", "", 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1413 @ <hr>
1414 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
1415 @ </div></form>
1416 db_end_transaction(0);
1417 style_finish_page();
1418
--- src/setup.c
+++ src/setup.c
@@ -118,10 +118,12 @@
118 setup_menu_entry("Settings", "setup_settings",
119 "Web interface to the \"fossil settings\" command");
120 }
121 setup_menu_entry("Timeline", "setup_timeline",
122 "Timeline display preferences");
123 setup_menu_entry("Tarballs and ZIPs", "setup_download",
124 "Preferences for auto-generated tarballs and ZIP files");
125 if( setup_user ){
126 setup_menu_entry("Login-Group", "setup_login_group",
127 "Manage single sign-on between this repository and others"
128 " on the same server");
129 setup_menu_entry("Tickets", "tktsetup",
@@ -140,12 +142,14 @@
142 setup_menu_entry("URL Aliases", "waliassetup",
143 "Configure URL aliases");
144 if( setup_user ){
145 setup_menu_entry("Notification", "setup_notification",
146 "Automatic notifications of changes via outbound email");
147 #if 0 /* Disabled for now. Does this even work? */
148 setup_menu_entry("Transfers", "xfersetup",
149 "Configure the transfer system for this repository");
150 #endif
151 }
152 setup_menu_entry("Skins", "setup_skin_admin",
153 "Select and/or modify the web interface \"skins\"");
154 setup_menu_entry("Moderation", "setup_modreq",
155 "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
@@ -421,56 +425,38 @@
425 };
426 multiple_choice_attribute(
427 "Enable hyperlinks base on User-Agent and/or Javascript",
428 "auto-hyperlink", "autohyperlink", "1",
429 count(azDefenseOpts)/2, azDefenseOpts);
430 @ <br>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431 entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
432 "auto-hyperlink-delay", "ah-delay", "50", 0);
433 @ <br>
434 onoff_attribute("Also require a mouse event before enabling hyperlinks",
435 "auto-hyperlink-mouseover", "ahmo", 0, 0);
436 @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
437 @ including user "nobody" if the request appears to be from a human.
438 @ Disabling hyperlinks helps prevent robots from walking your site and
439 @ soaking up all your CPU and bandwidth.
440 @ If this setting is "UserAgent only" (2) then the
441 @ UserAgent string is the only factor considered. If the value of this
442 @ setting is "UserAgent And Javascript" (1) then Javascript is added that
443 @ runs after the page loads and fills in the href= values of &lt;a&gt;
444 @ elements. In either case, &lt;a&gt; tags are not generated if the
445 @ UserAgent string indicates that the client is a robot.
446 @ (Property: "auto-hyperlink")</p>
447 @
448 @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
449 @ and "require a mouse event" should be turned on. These values only come
450 @ into play when the main auto-hyperlink settings is 2 ("UserAgent and
451 @ Javascript").
452 @ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")</p>
453 @
454 @ <p>To see if Javascript-base hyperlink enabling mechanism is working,
455 @ visit the <a href="%R/test-env">/test-env</a> page from a separate
456 @ web browser that is not logged in, even as "anonymous" and verify
457 @ that the "g.jsHref" value is "1".</p>
 
 
458 }
459
460 /*
461 ** WEBPAGE: setup_robot
462 **
@@ -488,19 +474,74 @@
474 @ <p>A Fossil website can have billions of pages in its tree, even for a
475 @ modest project. Many of those pages (examples: diffs and tarballs)
476 @ might be expensive to compute. A robot that tries to walk the entire
477 @ website can present a crippling CPU and bandwidth load.
478 @
479 @ <p>The settings on this page are intended to help administrators
480 @ defend against abusive robots.
481 @
482 @ <form action="%R/setup_robot" method="post"><div>
483 login_insert_csrf_secret();
484 @ <input type="submit" name="submit" value="Apply Changes"></p>
485 @ <hr>
486 @ <p><b>Do not allow robots access to these pages.</b><br>
487 @ If the page name matches the GLOB pattern of this setting, and the
488 @ users is "nobody", and the client has not previously passed a captcha
489 @ test to show that it is not a robot, then the page is not displayed.
490 @ A captcha test is is rendered instead.
491 @ The default value for this setting is:
492 @ <p>
493 @ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
494 @ <p>
495 @ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
496 @ /vpatch. The "annotate" tag covers /annotate and also /blame and
497 @ /praise. The "zip" covers itself and also /tarball and /sqlar.
498 @ If a tag has an "X" character appended (ex: "timelineX") then it only
499 @ applies if query parameters are such that the page is expensive
500 @ and/or unusual. In all other case, the tag should exactly match
501 @ the page name.
502 @
503 @ To disable robot restrictions, change this setting to "off".
504 @ (Property: robot-restrict)
505 @ <br>
506 textarea_attribute("", 2, 80,
507 "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
508
509 @ <p><b>Exception #1</b><br>
510 @ If "zipX" appears in the robot-restrict list above, then tarballs,
511 @ ZIP-archives, and SQL-archives may be downloaded by robots if
512 @ the check-in is a leaf (robot-zip-leaf):<br>
513 onoff_attribute("Allow tarballs for leaf check-ins",
514 "robot-zip-leaf", "rzleaf", 0, 0);
515
516 @ <p><b>Exception #2</b><br>
517 @ If "zipX" appears in the robot-restrict list above, then tarballs,
518 @ ZIP-archives, and SQL-archives may be downloaded by robots if
519 @ the check-in has one or more tags that match the following
520 @ list of GLOB patterns: (robot-zip-tag)<br>
521 textarea_attribute("", 2, 80,
522 "robot-zip-tag", "rztag", "", 0);
523
524 @ <p><b>Exception #3</b><br>
525 @ If the request URI matches any of the following
526 @ <a href="%R/re_rules">regular expressions</a> (one per line), then the
527 @ request is exempt from anti-robot defenses.
528 @ The regular expression is matched against the REQUEST_URI with the
529 @ SCRIPT_NAME prefix removed, and with QUERY_STRING appended following
530 @ a "?" if QUERY_STRING exists. (Property: robot-exception)<br>
531 textarea_attribute("", 3, 80,
532 "robot-exception", "rbexcept", "", 0);
533 @ <hr>
534 addAutoHyperlinkSettings();
535
536 @ <hr>
537 entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
538 "anoncookls", "840", 0);
539 @ <p>The number of minutes for which an anonymous login cookie is valid.
540 @ Set to zero to disable anonymous login.
541 @ (property: anon-cookie-lifespan)
542
543 @ <hr>
544 entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
545 "0.0", 0);
546 @ <p>Some expensive operations (such as computing tarballs, zip archives,
547 @ or annotation/blame pages) are prohibited if the load average on the host
@@ -508,37 +549,11 @@
549 @ computations here. Set this to 0.0 to disable the load average limit.
550 @ This limit is only enforced on Unix servers. On Linux systems,
551 @ access to the /proc virtual filesystem is required, which means this limit
552 @ might not work inside a chroot() jail.
553 @ (Property: "max-loadavg")</p>
554 @
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555 @ <hr>
556 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
557 @ </div></form>
558 db_end_transaction(0);
559 style_finish_page();
@@ -774,10 +789,17 @@
789 @ "anonymous" that will automatically fill in the CAPTCHA password.
790 @ This is less secure than forcing the user to do it manually, but is
791 @ probably secure enough and it is certainly more convenient for
792 @ anonymous users. (Property: "auto-captcha")</p>
793
794 @ <hr>
795 entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
796 "anoncookls", "840", 0);
797 @ <p>The number of minutes for which an anonymous login cookie is valid.
798 @ Set to zero to disable anonymous logins.
799 @ (property: anon-cookie-lifespan)
800
801 @ <hr>
802 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
803 @ </div></form>
804 db_end_transaction(0);
805 style_finish_page();
@@ -968,10 +990,15 @@
990 "1", "HH:MM:SS",
991 "2", "YYYY-MM-DD HH:MM",
992 "3", "YYMMDD HH:MM",
993 "4", "(off)"
994 };
995 static const char *const azLeafMark[] = {
996 "0", "No",
997 "1", "Yes",
998 "2", "Yes - with emphasis",
999 };
1000 login_check_credentials();
1001 if( !g.perm.Admin ){
1002 login_needed(0);
1003 return;
1004 }
@@ -1056,10 +1083,16 @@
1083 @ in a separate box (using CSS class "timelineDate") whenever the date
1084 @ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
1085 @ the complete date and time is shown on every timeline entry using the
1086 @ CSS class "timelineTime". (Property: "timeline-date-format")</p>
1087
1088 @ <hr>
1089 multiple_choice_attribute("Leaf Markings", "timeline-mark-leaves",
1090 "tml", "1", count(azLeafMark)/2, azLeafMark);
1091 @ <p>Should timeline entries for leaf check-ins be identified in the
1092 @ detail section. (Property: "timeline-mark-leaves")</p>
1093
1094 @ <hr>
1095 entry_attribute("Max timeline comment length", 6,
1096 "timeline-max-comment", "tmc", "0", 0);
1097 @ <p>The maximum length of a comment to be displayed in a timeline.
1098 @ "0" there is no length limit.
@@ -1158,11 +1191,11 @@
1191 continue;
1192 }
1193 onoff_attribute("", pSet->name,
1194 pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1195 is_truth(pSet->def), hasVersionableValue);
1196 @ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
1197 if( pSet->versionable ){
1198 @ (v)<br>
1199 } else {
1200 @ <br>
1201 }
@@ -1177,11 +1210,11 @@
1210 (db_get_versioned(pSet->name, NULL, NULL)!=0);
1211 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1212 continue;
1213 }
1214 @ <tr><td>
1215 @ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
1216 if( pSet->versionable ){
1217 @ (v)
1218 } else {
1219 @
1220 }
@@ -1198,11 +1231,11 @@
1231 if( pSet->width>0 && pSet->forceTextArea ){
1232 int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
1233 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1234 continue;
1235 }
1236 @ <a href='%R/help/%s(pSet->name)'>%s(pSet->name)</a>
1237 if( pSet->versionable ){
1238 @ (v)<br>
1239 } else {
1240 @ <br>
1241 }
@@ -1309,28 +1342,10 @@
1342 @ Omit the trailing "/".
1343 @ If this repo will not be set up as a persistent server and will not
1344 @ be sending email alerts, then leave this entry blank.
1345 @ Suggested value: "%h(g.zBaseURL)"
1346 @ (Property: "email-url")</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1347 @ <hr>
1348 entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
1349 @ <p>Enter the pathname of the page to display when the "Home" menu
1350 @ option is selected and when no pathname is
1351 @ specified in the URL. For example, if you visit the url:</p>
@@ -1408,10 +1423,66 @@
1423 @ <p>The default value is blank, meaning no added entries.
1424 @ (Property: sitemap-extra)
1425 @ <p>
1426 textarea_attribute("Custom Sitemap Entries", 8, 80,
1427 "sitemap-extra", "smextra", "", 0);
1428 @ <hr>
1429 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
1430 @ </div></form>
1431 db_end_transaction(0);
1432 style_finish_page();
1433 }
1434
1435 /*
1436 ** WEBPAGE: setup_download
1437 **
1438 ** The "Admin/Download" page. Requires Setup privilege.
1439 */
1440 void setup_download(void){
1441 login_check_credentials();
1442 if( !g.perm.Setup ){
1443 login_needed(0);
1444 return;
1445 }
1446
1447 style_set_current_feature("setup");
1448 style_header("Tarball and ZIP Downloads");
1449 db_begin_transaction();
1450 @ <form action="%R/setup_download" method="post"><div>
1451 login_insert_csrf_secret();
1452 @ <input type="submit" name="submit" value="Apply Changes"></p>
1453 @ <hr>
1454 entry_attribute("Tarball and ZIP Name Prefix", 20, "short-project-name",
1455 "spn", "", 0);
1456 @ <p>This is used as a prefix for the names of generated tarballs and
1457 @ ZIP archive. Keep this prefix brief and use only lower-case ASCII
1458 @ characters, digits, "_", "-" in the name. If this setting is blank,
1459 @ then the full <a href='%R/help/project-name'>project-name</a> setting
1460 @ is used instead.
1461 @ (Property: "short-project-name")
1462 @ </p>
1463 @ <hr>
1464 @ <p><b>Configuration for the <a href="%R/download">/download</a> page.</b>
1465 @ <p>The value is a TCL list divided into groups of four tokens:
1466 @ <ol>
1467 @ <li> Maximum number of matches (COUNT).
1468 @ <li> Tag to match using glob (TAG).
1469 @ <li> Maximum age of check-ins to match (MAX_AGE).
1470 @ <li> Comment to apply to matches (COMMENT).
1471 @ </ol>
1472 @ Each 4-tuple will match zero or more check-ins. The /download page
1473 @ displays the union of matches from all 4-tuples.
1474 @ See the <a href="%R/help/suggested-downloads">suggested-downloads</a>
1475 @ setting documentation for further detail.
1476 @ <p>
1477 @ The /download page is omitted from the <a href="%R/sitemap">/sitemap</a>
1478 @ if the first token is "0" or "off" or "no". The default value
1479 @ for this setting is "off".
1480 @ (Property: <a href="%R/help/suggested-downloads">suggested-downloads</a>)
1481 @ <p>
1482 textarea_attribute("", 4, 80,
1483 "suggested-downloads", "sgtrlst", "off", 0);
1484 @ <hr>
1485 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
1486 @ </div></form>
1487 db_end_transaction(0);
1488 style_finish_page();
1489
--- src/sitemap.c
+++ src/sitemap.c
@@ -132,10 +132,13 @@
132132
@ <li>%z(href("%R/uvlist"))Unversioned Files</a>
133133
if( g.perm.Write && zEditGlob[0]!=0 ){
134134
@ <li>%z(href("%R/fileedit"))On-line File Editor</li>
135135
}
136136
@ </ul>
137
+ }
138
+ if( g.perm.Zip && db_get_boolean("suggested-downloads",0)!=0 ){
139
+ @ <li>%z(href("%R/download"))Tarballs and ZIPs</a>
137140
}
138141
if( g.perm.Read ){
139142
@ <li>%z(href("%R/timeline"))Project Timeline</a>
140143
@ <ul>
141144
@ <li>%z(href("%R/reports"))Activity Reports</a></li>
142145
--- src/sitemap.c
+++ src/sitemap.c
@@ -132,10 +132,13 @@
132 @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
133 if( g.perm.Write && zEditGlob[0]!=0 ){
134 @ <li>%z(href("%R/fileedit"))On-line File Editor</li>
135 }
136 @ </ul>
 
 
 
137 }
138 if( g.perm.Read ){
139 @ <li>%z(href("%R/timeline"))Project Timeline</a>
140 @ <ul>
141 @ <li>%z(href("%R/reports"))Activity Reports</a></li>
142
--- src/sitemap.c
+++ src/sitemap.c
@@ -132,10 +132,13 @@
132 @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
133 if( g.perm.Write && zEditGlob[0]!=0 ){
134 @ <li>%z(href("%R/fileedit"))On-line File Editor</li>
135 }
136 @ </ul>
137 }
138 if( g.perm.Zip && db_get_boolean("suggested-downloads",0)!=0 ){
139 @ <li>%z(href("%R/download"))Tarballs and ZIPs</a>
140 }
141 if( g.perm.Read ){
142 @ <li>%z(href("%R/timeline"))Project Timeline</a>
143 @ <ul>
144 @ <li>%z(href("%R/reports"))Activity Reports</a></li>
145
+2 -2
--- src/skins.c
+++ src/skins.c
@@ -1345,11 +1345,11 @@
13451345
}
13461346
13471347
/*
13481348
** WEBPAGE: skins
13491349
**
1350
-** Show a list of all of the built-in skins, plus the respository skin,
1350
+** Show a list of all of the built-in skins, plus the repository skin,
13511351
** and provide the user with an opportunity to change to any of them.
13521352
*/
13531353
void skins_page(void){
13541354
int i;
13551355
char *zBase = fossil_strdup(g.zTop);
@@ -1373,11 +1373,11 @@
13731373
@ you are using a draft skin,
13741374
}else{
13751375
@ this fossil instance was started with a hard-coded skin
13761376
@ value
13771377
}
1378
- @ which supercedes any option selected below. A skin selected
1378
+ @ which supersedes any option selected below. A skin selected
13791379
@ below will be recorded in your
13801380
@ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie
13811381
@ but will not be used so long as the site has a
13821382
@ higher-priority skin in place.
13831383
@ </p>
13841384
--- src/skins.c
+++ src/skins.c
@@ -1345,11 +1345,11 @@
1345 }
1346
1347 /*
1348 ** WEBPAGE: skins
1349 **
1350 ** Show a list of all of the built-in skins, plus the respository skin,
1351 ** and provide the user with an opportunity to change to any of them.
1352 */
1353 void skins_page(void){
1354 int i;
1355 char *zBase = fossil_strdup(g.zTop);
@@ -1373,11 +1373,11 @@
1373 @ you are using a draft skin,
1374 }else{
1375 @ this fossil instance was started with a hard-coded skin
1376 @ value
1377 }
1378 @ which supercedes any option selected below. A skin selected
1379 @ below will be recorded in your
1380 @ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie
1381 @ but will not be used so long as the site has a
1382 @ higher-priority skin in place.
1383 @ </p>
1384
--- src/skins.c
+++ src/skins.c
@@ -1345,11 +1345,11 @@
1345 }
1346
1347 /*
1348 ** WEBPAGE: skins
1349 **
1350 ** Show a list of all of the built-in skins, plus the repository skin,
1351 ** and provide the user with an opportunity to change to any of them.
1352 */
1353 void skins_page(void){
1354 int i;
1355 char *zBase = fossil_strdup(g.zTop);
@@ -1373,11 +1373,11 @@
1373 @ you are using a draft skin,
1374 }else{
1375 @ this fossil instance was started with a hard-coded skin
1376 @ value
1377 }
1378 @ which supersedes any option selected below. A skin selected
1379 @ below will be recorded in your
1380 @ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie
1381 @ but will not be used so long as the site has a
1382 @ higher-priority skin in place.
1383 @ </p>
1384
+15 -5
--- src/stash.c
+++ src/stash.c
@@ -550,22 +550,26 @@
550550
**
551551
** Update to the baseline check-out for STASHID then apply the
552552
** changes of STASHID. Keep STASHID so that it can be reused
553553
** This command is undoable.
554554
**
555
-** > fossil stash drop|rm ?STASHID? ?-a|--all?
555
+** > fossil stash drop|rm ?STASHIDs...? ?-a|--all?
556556
**
557
-** Forget everything about STASHID. Forget the whole stash if the
558
-** -a|--all flag is used. Individual drops are undoable but -a|--all
559
-** is not.
557
+** Forget everything about the given STASHIDs. Forget the whole
558
+** stash if the -a|--all flag is used. Individual drops are
559
+** undoable but -a|--all is not.
560560
**
561561
** > fossil stash diff ?STASHID? ?DIFF-OPTIONS?
562562
** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
563563
**
564564
** Show diffs of the current working directory and what that
565565
** directory would be if STASHID were applied. With gdiff,
566566
** gdiff-command is used instead of internal diff logic.
567
+**
568
+** > fossil stash rename STASHID NEW-NAME
569
+**
570
+** Change the description of the given STASHID entry to NEW-NAME.
567571
*/
568572
void stash_cmd(void){
569573
const char *zCmd;
570574
int nCmd;
571575
int stashid = 0;
@@ -771,11 +775,17 @@
771775
}
772776
diff_options(&DCfg, zCmd[0]=='g', 0);
773777
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
774778
stash_diff(stashid, fBaseline, &DCfg);
775779
}else
776
- if( strncmp(zCmd, "help", nCmd)==0 ){
780
+ if( strncmp(zCmd, "rename", nCmd)==0 ){
781
+ if( g.argc!=5 ) usage("rename STASHID NAME");
782
+ stashid = stash_get_id(g.argv[3]);
783
+ db_multi_exec("UPDATE STASH SET COMMENT=%Q WHERE stashid=%d",
784
+ g.argv[4], stashid);
785
+ }
786
+ else if( strncmp(zCmd, "help", nCmd)==0 ){
777787
g.argv[1] = "help";
778788
g.argv[2] = "stash";
779789
g.argc = 3;
780790
help_cmd();
781791
}else
782792
--- src/stash.c
+++ src/stash.c
@@ -550,22 +550,26 @@
550 **
551 ** Update to the baseline check-out for STASHID then apply the
552 ** changes of STASHID. Keep STASHID so that it can be reused
553 ** This command is undoable.
554 **
555 ** > fossil stash drop|rm ?STASHID? ?-a|--all?
556 **
557 ** Forget everything about STASHID. Forget the whole stash if the
558 ** -a|--all flag is used. Individual drops are undoable but -a|--all
559 ** is not.
560 **
561 ** > fossil stash diff ?STASHID? ?DIFF-OPTIONS?
562 ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
563 **
564 ** Show diffs of the current working directory and what that
565 ** directory would be if STASHID were applied. With gdiff,
566 ** gdiff-command is used instead of internal diff logic.
 
 
 
 
567 */
568 void stash_cmd(void){
569 const char *zCmd;
570 int nCmd;
571 int stashid = 0;
@@ -771,11 +775,17 @@
771 }
772 diff_options(&DCfg, zCmd[0]=='g', 0);
773 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
774 stash_diff(stashid, fBaseline, &DCfg);
775 }else
776 if( strncmp(zCmd, "help", nCmd)==0 ){
 
 
 
 
 
 
777 g.argv[1] = "help";
778 g.argv[2] = "stash";
779 g.argc = 3;
780 help_cmd();
781 }else
782
--- src/stash.c
+++ src/stash.c
@@ -550,22 +550,26 @@
550 **
551 ** Update to the baseline check-out for STASHID then apply the
552 ** changes of STASHID. Keep STASHID so that it can be reused
553 ** This command is undoable.
554 **
555 ** > fossil stash drop|rm ?STASHIDs...? ?-a|--all?
556 **
557 ** Forget everything about the given STASHIDs. Forget the whole
558 ** stash if the -a|--all flag is used. Individual drops are
559 ** undoable but -a|--all is not.
560 **
561 ** > fossil stash diff ?STASHID? ?DIFF-OPTIONS?
562 ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
563 **
564 ** Show diffs of the current working directory and what that
565 ** directory would be if STASHID were applied. With gdiff,
566 ** gdiff-command is used instead of internal diff logic.
567 **
568 ** > fossil stash rename STASHID NEW-NAME
569 **
570 ** Change the description of the given STASHID entry to NEW-NAME.
571 */
572 void stash_cmd(void){
573 const char *zCmd;
574 int nCmd;
575 int stashid = 0;
@@ -771,11 +775,17 @@
775 }
776 diff_options(&DCfg, zCmd[0]=='g', 0);
777 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
778 stash_diff(stashid, fBaseline, &DCfg);
779 }else
780 if( strncmp(zCmd, "rename", nCmd)==0 ){
781 if( g.argc!=5 ) usage("rename STASHID NAME");
782 stashid = stash_get_id(g.argv[3]);
783 db_multi_exec("UPDATE STASH SET COMMENT=%Q WHERE stashid=%d",
784 g.argv[4], stashid);
785 }
786 else if( strncmp(zCmd, "help", nCmd)==0 ){
787 g.argv[1] = "help";
788 g.argv[2] = "stash";
789 g.argc = 3;
790 help_cmd();
791 }else
792
+21 -62
--- src/style.c
+++ src/style.c
@@ -478,11 +478,11 @@
478478
/*
479479
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
480480
** Javascript module, and generates HTML elements with the following IDs:
481481
**
482482
** TARGETID: The <span> wrapper around TEXT.
483
-** copy-TARGETID: The <span> for the copy button.
483
+** copy-TARGETID: The <button> for the copy button.
484484
**
485485
** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
486486
**
487487
** The COPYLENGTH argument defines the length of the substring of TEXT copied to
488488
** clipboard:
@@ -510,18 +510,20 @@
510510
if( cchLength==1 ) cchLength = hash_digits(0);
511511
else if( cchLength==2 ) cchLength = hash_digits(1);
512512
if( !bFlipped ){
513513
const char *zBtnFmt =
514514
"<span class=\"nobr\">"
515
- "<span "
516
- "class=\"copy-button\" "
517
- "id=\"copy-%h\" "
518
- "data-copytarget=\"%h\" "
519
- "data-copylength=\"%d\">"
520
- "</span>"
515
+ "<button "
516
+ "class=\"copy-button\" "
517
+ "id=\"copy-%h\" "
518
+ "data-copytarget=\"%h\" "
519
+ "data-copylength=\"%d\">"
520
+ "<span>"
521
+ "</span>"
522
+ "</button>"
521523
"<span id=\"%h\">"
522
- "%s"
524
+ "%s"
523525
"</span>"
524526
"</span>";
525527
if( bOutputCGI ){
526528
cgi_printf(
527529
zBtnFmt/*works-like:"%h%h%d%h%s"*/,
@@ -533,18 +535,20 @@
533535
}
534536
}else{
535537
const char *zBtnFmt =
536538
"<span class=\"nobr\">"
537539
"<span id=\"%h\">"
538
- "%s"
539
- "</span>"
540
- "<span "
541
- "class=\"copy-button copy-button-flipped\" "
542
- "id=\"copy-%h\" "
543
- "data-copytarget=\"%h\" "
544
- "data-copylength=\"%d\">"
545
- "</span>"
540
+ "%s"
541
+ "</span>"
542
+ "<button "
543
+ "class=\"copy-button copy-button-flipped\" "
544
+ "id=\"copy-%h\" "
545
+ "data-copytarget=\"%h\" "
546
+ "data-copylength=\"%d\">"
547
+ "<span>"
548
+ "</span>"
549
+ "</button>"
546550
"</span>";
547551
if( bOutputCGI ){
548552
cgi_printf(
549553
zBtnFmt/*works-like:"%h%s%h%h%d"*/,
550554
zTargetId,zText,zTargetId,zTargetId,cchLength);
@@ -1386,55 +1390,10 @@
13861390
*/
13871391
void page_test_env(void){
13881392
webpage_error("");
13891393
}
13901394
1391
-/*
1392
-** WEBPAGE: honeypot
1393
-** This page is a honeypot for spiders and bots.
1394
-*/
1395
-void honeypot_page(void){
1396
- unsigned int uSeed = captcha_seed();
1397
- const char *zDecoded = captcha_decode(uSeed, 0);
1398
- int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1399
- char *zCaptcha = captcha_render(zDecoded);
1400
- style_header("I think you are a robot");
1401
- @ <p>You seem like a robot.</p>
1402
- @
1403
- @ <p>Is that incorrect? Are you really human?
1404
- @ If so, please prove it by transcribing the captcha text
1405
- @ into the entry box below and pressing "Submit".
1406
- @ <form action="%R/login" method="post">
1407
- @ <input type="hidden" id="u" name="u" value="anonymous">
1408
- @ <p>
1409
- @ Captcha: <input type="text" id="p" name="p" value="">
1410
- @ <input type="submit" name="in" value="Submit">
1411
- @
1412
- @ <p>Alternatively, you can <a href="%R/login">log in</a> using an
1413
- @ existing userid.
1414
- @
1415
- @ <p><input type="hidden" name="cs" value="%u(uSeed)">
1416
- @ <div class="captcha"><table class="captcha"><tr><td>\
1417
- @ <pre class="captcha">
1418
- @ %h(zCaptcha)
1419
- @ </pre></td></tr></table>
1420
- if( bAutoCaptcha ) {
1421
- @ <input type="button" value="Fill out captcha" id='autofillButton' \
1422
- @ data-af='%s(zDecoded)'>
1423
- builtin_request_js("login.js");
1424
- }
1425
- @ </div>
1426
- free(zCaptcha);
1427
- @
1428
- @ <p>We regret this inconvenience. However, robots have become so
1429
- @ prolific and so aggressive that they will soak up too much CPU time
1430
- @ and network bandwidth on our servers if allowed to run unchecked.
1431
- @ Your cooperation in demonstrating that you are human is
1432
- @ appreciated.
1433
- style_finish_page();
1434
-}
1435
-
14361395
/*
14371396
** Webpages that encounter an error due to missing or incorrect
14381397
** query parameters can jump to this routine to render an error
14391398
** message screen.
14401399
**
@@ -1483,11 +1442,11 @@
14831442
@ g.zHttpsURL = %h(g.zHttpsURL)<br>
14841443
@ g.zTop = %h(g.zTop)<br>
14851444
@ g.zPath = %h(g.zPath)<br>
14861445
@ g.userUid = %d(g.userUid)<br>
14871446
@ g.zLogin = %h(g.zLogin)<br>
1488
- @ g.isHuman = %d(g.isHuman)<br>
1447
+ @ g.isRobot = %d(g.isRobot)<br>
14891448
@ g.jsHref = %d(g.jsHref)<br>
14901449
if( g.zLocalRoot ){
14911450
@ g.zLocalRoot = %h(g.zLocalRoot)<br>
14921451
}else{
14931452
@ g.zLocalRoot = <i>none</i><br>
14941453
--- src/style.c
+++ src/style.c
@@ -478,11 +478,11 @@
478 /*
479 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
480 ** Javascript module, and generates HTML elements with the following IDs:
481 **
482 ** TARGETID: The <span> wrapper around TEXT.
483 ** copy-TARGETID: The <span> for the copy button.
484 **
485 ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
486 **
487 ** The COPYLENGTH argument defines the length of the substring of TEXT copied to
488 ** clipboard:
@@ -510,18 +510,20 @@
510 if( cchLength==1 ) cchLength = hash_digits(0);
511 else if( cchLength==2 ) cchLength = hash_digits(1);
512 if( !bFlipped ){
513 const char *zBtnFmt =
514 "<span class=\"nobr\">"
515 "<span "
516 "class=\"copy-button\" "
517 "id=\"copy-%h\" "
518 "data-copytarget=\"%h\" "
519 "data-copylength=\"%d\">"
520 "</span>"
 
 
521 "<span id=\"%h\">"
522 "%s"
523 "</span>"
524 "</span>";
525 if( bOutputCGI ){
526 cgi_printf(
527 zBtnFmt/*works-like:"%h%h%d%h%s"*/,
@@ -533,18 +535,20 @@
533 }
534 }else{
535 const char *zBtnFmt =
536 "<span class=\"nobr\">"
537 "<span id=\"%h\">"
538 "%s"
539 "</span>"
540 "<span "
541 "class=\"copy-button copy-button-flipped\" "
542 "id=\"copy-%h\" "
543 "data-copytarget=\"%h\" "
544 "data-copylength=\"%d\">"
545 "</span>"
 
 
546 "</span>";
547 if( bOutputCGI ){
548 cgi_printf(
549 zBtnFmt/*works-like:"%h%s%h%h%d"*/,
550 zTargetId,zText,zTargetId,zTargetId,cchLength);
@@ -1386,55 +1390,10 @@
1386 */
1387 void page_test_env(void){
1388 webpage_error("");
1389 }
1390
1391 /*
1392 ** WEBPAGE: honeypot
1393 ** This page is a honeypot for spiders and bots.
1394 */
1395 void honeypot_page(void){
1396 unsigned int uSeed = captcha_seed();
1397 const char *zDecoded = captcha_decode(uSeed, 0);
1398 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1399 char *zCaptcha = captcha_render(zDecoded);
1400 style_header("I think you are a robot");
1401 @ <p>You seem like a robot.</p>
1402 @
1403 @ <p>Is that incorrect? Are you really human?
1404 @ If so, please prove it by transcribing the captcha text
1405 @ into the entry box below and pressing "Submit".
1406 @ <form action="%R/login" method="post">
1407 @ <input type="hidden" id="u" name="u" value="anonymous">
1408 @ <p>
1409 @ Captcha: <input type="text" id="p" name="p" value="">
1410 @ <input type="submit" name="in" value="Submit">
1411 @
1412 @ <p>Alternatively, you can <a href="%R/login">log in</a> using an
1413 @ existing userid.
1414 @
1415 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
1416 @ <div class="captcha"><table class="captcha"><tr><td>\
1417 @ <pre class="captcha">
1418 @ %h(zCaptcha)
1419 @ </pre></td></tr></table>
1420 if( bAutoCaptcha ) {
1421 @ <input type="button" value="Fill out captcha" id='autofillButton' \
1422 @ data-af='%s(zDecoded)'>
1423 builtin_request_js("login.js");
1424 }
1425 @ </div>
1426 free(zCaptcha);
1427 @
1428 @ <p>We regret this inconvenience. However, robots have become so
1429 @ prolific and so aggressive that they will soak up too much CPU time
1430 @ and network bandwidth on our servers if allowed to run unchecked.
1431 @ Your cooperation in demonstrating that you are human is
1432 @ appreciated.
1433 style_finish_page();
1434 }
1435
1436 /*
1437 ** Webpages that encounter an error due to missing or incorrect
1438 ** query parameters can jump to this routine to render an error
1439 ** message screen.
1440 **
@@ -1483,11 +1442,11 @@
1483 @ g.zHttpsURL = %h(g.zHttpsURL)<br>
1484 @ g.zTop = %h(g.zTop)<br>
1485 @ g.zPath = %h(g.zPath)<br>
1486 @ g.userUid = %d(g.userUid)<br>
1487 @ g.zLogin = %h(g.zLogin)<br>
1488 @ g.isHuman = %d(g.isHuman)<br>
1489 @ g.jsHref = %d(g.jsHref)<br>
1490 if( g.zLocalRoot ){
1491 @ g.zLocalRoot = %h(g.zLocalRoot)<br>
1492 }else{
1493 @ g.zLocalRoot = <i>none</i><br>
1494
--- src/style.c
+++ src/style.c
@@ -478,11 +478,11 @@
478 /*
479 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
480 ** Javascript module, and generates HTML elements with the following IDs:
481 **
482 ** TARGETID: The <span> wrapper around TEXT.
483 ** copy-TARGETID: The <button> for the copy button.
484 **
485 ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
486 **
487 ** The COPYLENGTH argument defines the length of the substring of TEXT copied to
488 ** clipboard:
@@ -510,18 +510,20 @@
510 if( cchLength==1 ) cchLength = hash_digits(0);
511 else if( cchLength==2 ) cchLength = hash_digits(1);
512 if( !bFlipped ){
513 const char *zBtnFmt =
514 "<span class=\"nobr\">"
515 "<button "
516 "class=\"copy-button\" "
517 "id=\"copy-%h\" "
518 "data-copytarget=\"%h\" "
519 "data-copylength=\"%d\">"
520 "<span>"
521 "</span>"
522 "</button>"
523 "<span id=\"%h\">"
524 "%s"
525 "</span>"
526 "</span>";
527 if( bOutputCGI ){
528 cgi_printf(
529 zBtnFmt/*works-like:"%h%h%d%h%s"*/,
@@ -533,18 +535,20 @@
535 }
536 }else{
537 const char *zBtnFmt =
538 "<span class=\"nobr\">"
539 "<span id=\"%h\">"
540 "%s"
541 "</span>"
542 "<button "
543 "class=\"copy-button copy-button-flipped\" "
544 "id=\"copy-%h\" "
545 "data-copytarget=\"%h\" "
546 "data-copylength=\"%d\">"
547 "<span>"
548 "</span>"
549 "</button>"
550 "</span>";
551 if( bOutputCGI ){
552 cgi_printf(
553 zBtnFmt/*works-like:"%h%s%h%h%d"*/,
554 zTargetId,zText,zTargetId,zTargetId,cchLength);
@@ -1386,55 +1390,10 @@
1390 */
1391 void page_test_env(void){
1392 webpage_error("");
1393 }
1394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1395 /*
1396 ** Webpages that encounter an error due to missing or incorrect
1397 ** query parameters can jump to this routine to render an error
1398 ** message screen.
1399 **
@@ -1483,11 +1442,11 @@
1442 @ g.zHttpsURL = %h(g.zHttpsURL)<br>
1443 @ g.zTop = %h(g.zTop)<br>
1444 @ g.zPath = %h(g.zPath)<br>
1445 @ g.userUid = %d(g.userUid)<br>
1446 @ g.zLogin = %h(g.zLogin)<br>
1447 @ g.isRobot = %d(g.isRobot)<br>
1448 @ g.jsHref = %d(g.jsHref)<br>
1449 if( g.zLocalRoot ){
1450 @ g.zLocalRoot = %h(g.zLocalRoot)<br>
1451 }else{
1452 @ g.zLocalRoot = <i>none</i><br>
1453
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -218,11 +218,11 @@
218218
}
219219
}
220220
if( zCol ){
221221
db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222222
zCol, zValue, rid);
223
- if( tagid==TAG_COMMENT ){
223
+ if( tagid==TAG_COMMENT && zValue!=0 ){
224224
char *zCopy = fossil_strdup(zValue);
225225
backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
226226
free(zCopy);
227227
}
228228
}
229229
--- src/tag.c
+++ src/tag.c
@@ -218,11 +218,11 @@
218 }
219 }
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT ){
224 char *zCopy = fossil_strdup(zValue);
225 backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
226 free(zCopy);
227 }
228 }
229
--- src/tag.c
+++ src/tag.c
@@ -218,11 +218,11 @@
218 }
219 }
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT && zValue!=0 ){
224 char *zCopy = fossil_strdup(zValue);
225 backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
226 free(zCopy);
227 }
228 }
229
+531 -34
--- src/tar.c
+++ src/tar.c
@@ -31,10 +31,65 @@
3131
char *zPrevDir; /* Name of directory for previous entry */
3232
int nPrevDirAlloc; /* size of zPrevDir */
3333
Blob pax; /* PAX data */
3434
} tball;
3535
36
+/*
37
+** Convert a string so that it contains only lower-case ASCII, digits,
38
+** "_" and "-". Changes are made in-place.
39
+*/
40
+static void sanitize_name(char *zName){
41
+ int i;
42
+ char c;
43
+ for(i=0; (c = zName[i])!=0; i++){
44
+ if( fossil_isupper(c) ){
45
+ zName[i] = fossil_tolower(c);
46
+ }else if( !fossil_isalnum(c) && c!='_' && c!='-' ){
47
+ if( c<=0x7f ){
48
+ zName[i] = '_';
49
+ }else{
50
+ /* 123456789 123456789 123456 */
51
+ zName[i] = "abcdefghijklmnopqrstuvwxyz"[(unsigned)c%26];
52
+ }
53
+ }
54
+ }
55
+}
56
+
57
+/*
58
+** Compute a sensible base-name for an archive file (tarball, ZIP, or SQLAR)
59
+** based on the rid of the check-in contained in that file.
60
+**
61
+** PROJECTNAME-DATETIME-HASHPREFIX
62
+**
63
+** So that the name will be safe to use as a URL or a filename on any system,
64
+** the name is only allowed to contain lower-case ASCII alphabetics,
65
+** digits, '_' and '-'. Upper-case ASCII is converted to lower-case. All
66
+** other bytes are mapped into a lower-case alphabetic.
67
+**
68
+** The value returned is obtained from mprintf() or fossil_strdup() and should
69
+** be released by the caller using fossil_free().
70
+*/
71
+char *archive_base_name(int rid){
72
+ char *zPrefix;
73
+ char *zName;
74
+ zPrefix = db_get("short-project-name",0);
75
+ if( zPrefix==0 || zPrefix[0]==0 ){
76
+ zPrefix = db_get("project-name","unnamed");
77
+ }
78
+ zName = db_text(0,
79
+ "SELECT %Q||"
80
+ " strftime('-%%Y%%m%%d%%H%%M%%S-',event.mtime)||"
81
+ " substr(blob.uuid,1,10)"
82
+ " FROM blob, event LEFT JOIN config"
83
+ " WHERE blob.rid=%d"
84
+ " AND event.objid=%d"
85
+ " AND config.name='project-name'",
86
+ zPrefix, rid, rid);
87
+ fossil_free(zPrefix);
88
+ sanitize_name(zName);
89
+ return zName;
90
+}
3691
3792
/*
3893
** field lengths of 'ustar' name and prefix fields.
3994
*/
4095
#define USTAR_NAME_LEN 100
@@ -653,19 +708,11 @@
653708
if( fossil_strcmp("/dev/null",zOut)==0 || fossil_strcmp("",zOut)==0 ){
654709
zOut = 0;
655710
}
656711
657712
if( zName==0 ){
658
- zName = db_text("default-name",
659
- "SELECT replace(%Q,' ','_') "
660
- " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
661
- " || substr(blob.uuid, 1, 10)"
662
- " FROM event, blob"
663
- " WHERE event.objid=%d"
664
- " AND blob.rid=%d",
665
- db_get("project-name", "unnamed"), rid, rid
666
- );
713
+ zName = archive_base_name(rid);
667714
}
668715
tarball_of_checkin(rid, zOut ? &tarball : 0,
669716
zName, pInclude, pExclude, listFlag);
670717
glob_free(pInclude);
671718
glob_free(pExclude);
@@ -675,45 +722,171 @@
675722
blob_reset(&tarball);
676723
}
677724
}
678725
679726
/*
680
-** Check to see if the input string is of the form:
681
-**
682
-** check-in-name/filename.ext
683
-**
684
-** In other words, check to see if the input contains a single '/'
685
-** character that separates a valid check-in name from a filename.
686
-**
687
-** If the condition is true, return the check-in name and set the
688
-** input string to be the filename.
689
-**
690
-** If the condition is false, return NULL
727
+** This is a helper routine for tar_uuid_from_name(). It handles
728
+** the case where *pzName contains no "/" character. Check for
729
+** format (3). Return the hash if the name matches format (3),
730
+** or return NULL if it does not.
731
+*/
732
+static char *format_three_parser(const char *zName){
733
+ int iDot = 0; /* Index in zName[] of the first '.' */
734
+ int iDash1 = 0; /* Index in zName[] of the '-' before the timestamp */
735
+ int iDash2 = 0; /* Index in zName[] of the '-' between timestamp and hash */
736
+ int nHash; /* Size of the hash */
737
+ char *zHash; /* A copy of the hash value */
738
+ char *zDate; /* Copy of the timestamp */
739
+ char *zUuid; /* Final result */
740
+ int i; /* Loop query */
741
+ Stmt q; /* Query to verify that hash and timestamp agree */
742
+
743
+ for(i=0; zName[i]; i++){
744
+ char c = zName[i];
745
+ if( c=='.' ){ iDot = i; break; }
746
+ if( c=='-' ){ iDash1 = iDash2; iDash2 = i; }
747
+ if( !fossil_isalnum(c) && c!='_' && c!='-' ){ break; }
748
+ }
749
+ if( iDot==0 ) return 0;
750
+ if( iDash1==0 ) return 0;
751
+ nHash = iDot - iDash2 - 1;
752
+ if( nHash<8 ) return 0; /* HASH value too short */
753
+ if( (iDash2 - iDash1)!=15 ) return 0; /* Wrong timestamp size */
754
+ zHash = fossil_strndup(&zName[iDash2+1], nHash);
755
+ zDate = fossil_strndup(&zName[iDash1+1], 14);
756
+ db_prepare(&q,
757
+ "SELECT blob.uuid"
758
+ " FROM blob JOIN event ON event.objid=blob.rid"
759
+ " WHERE blob.uuid GLOB '%q*'"
760
+ " AND strftime('%%Y%%m%%d%%H%%M%%S',event.mtime)='%q'",
761
+ zHash, zDate
762
+ );
763
+ fossil_free(zHash);
764
+ fossil_free(zDate);
765
+ if( db_step(&q)==SQLITE_ROW ){
766
+ zUuid = fossil_strdup(db_column_text(&q,0));
767
+ }else{
768
+ zUuid = 0;
769
+ }
770
+ db_finalize(&q);
771
+ return zUuid;
772
+}
773
+
774
+/*
775
+** Check to see if the input string is of one of the following
776
+** two the forms:
777
+**
778
+** check-in-name/filename.ext (1)
779
+** tag-name/check-in-name/filename.ext (2)
780
+** project-datetime-hash.ext (3)
781
+**
782
+** In other words, check to see if the input string contains either
783
+** a check-in name or a tag-name and a check-in name separated by
784
+** a slash. There must be between 0 or 2 "/" characters. In the
785
+** second form, tag-name must be an individual tag (not a branch-tag)
786
+** that is found on the check-in identified by the check-in-name.
787
+**
788
+** If the condition is true, then:
789
+**
790
+** * Make *pzName point to the filename suffix only
791
+** * return a copy of the check-in name in memory from mprintf().
792
+**
793
+** If the condition is false, leave *pzName unchanged and return either
794
+** NULL or an empty string. Normally NULL is returned, however an
795
+** empty string is returned for format (2) if check-in-name does not
796
+** match tag-name.
797
+**
798
+** Format (2) is specifically designed to allow URLs like this:
799
+**
800
+** /tarball/release/UUID/PROJECT.tar.gz
801
+**
802
+** Such URLs will pass through most anti-robot filters because of the
803
+** "/tarball/release" prefix will match the suggested "robot-exception"
804
+** pattern and can still refer to an historic release rather than just
805
+** the most recent release.
806
+**
807
+** Format (3) is designed to allow URLs like this:
808
+**
809
+** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz
810
+**
811
+** In other words, filename itself contains sufficient information to
812
+** uniquely identify the check-in, including a timestamp of the form
813
+** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp
814
+** and hash must immediately preceed the first "." in the name.
691815
*/
692816
char *tar_uuid_from_name(char **pzName){
693
- char *zName = *pzName;
694
- int i, n;
695
- for(i=n=0; zName[i]; i++){
817
+ char *zName = *pzName; /* Original input */
818
+ int n1 = 0; /* Bytes in first prefix (tag-name) */
819
+ int n2 = 0; /* Bytes in second prefix (check-in-name) */
820
+ int n = 0; /* max(n1,n2) */
821
+ int i; /* Loop counter */
822
+ for(i=n1=n2=0; zName[i]; i++){
696823
if( zName[i]=='/' ){
697
- if( n==0 ) n = i;
698
- else return 0;
699
- }
700
- }
701
- if( n==0 ) return 0;
702
- if( zName[n+1]==0 ) return 0;
703
- zName[n] = 0;
704
- *pzName = fossil_strdup(&zName[n+1]);
705
- return zName;
824
+ if( n1==0 ){
825
+ n = n1 = i;
826
+ }else if( n2==0 ){
827
+ n = n2 = i;
828
+ }else{
829
+ return 0; /* More than two "/" characters seen */
830
+ }
831
+ }
832
+ }
833
+ if( n1==0 ){
834
+ /* Check for format (3) */
835
+ return format_three_parser(*pzName);
836
+ }
837
+ if( zName[n+1]==0 ){
838
+ return 0; /* No filename suffix */
839
+ }
840
+ if( n2==0 ){
841
+ /* Format (1): check-in name only. The check-in-name is not verified */
842
+ zName[n1] = 0;
843
+ *pzName = fossil_strdup(&zName[n1+1]);
844
+ return zName;
845
+ }else if( n2>n1+1 ){
846
+ /* Format (2): tag-name/check-in-name. Verify that check-in-name is real
847
+ ** and that the check-in has the tag named by tag-name.
848
+ */
849
+ char *zCkin = mprintf("%.*s", n2-n1-1, &zName[n1+1]);
850
+ char *zTag;
851
+ int rid = symbolic_name_to_rid(zCkin,"ci");
852
+ int hasTag;
853
+ if( rid<=0 ){
854
+ fossil_free(zCkin);
855
+ return fossil_strdup("");
856
+ }
857
+ zTag = mprintf("%.*s", n1, zName);
858
+ hasTag = db_exists(
859
+ "SELECT 1 FROM tagxref, tag"
860
+ " WHERE tagxref.rid=%d"
861
+ " AND tag.tagid=tagxref.tagid"
862
+ " AND tagxref.tagtype=1"
863
+ " AND tag.tagname='sym-%q'",
864
+ rid, zTag
865
+ );
866
+ fossil_free(zTag);
867
+ if( !hasTag ){
868
+ fossil_free(zCkin);
869
+ return fossil_strdup("");
870
+ }
871
+ *pzName = fossil_strdup(&zName[n2+1]);
872
+ return zCkin;
873
+ }else{
874
+ return 0;
875
+ }
706876
}
707877
708878
/*
709879
** WEBPAGE: tarball
710
-** URL: /tarball/[VERSION/]NAME.tar.gz
880
+** URL: /tarball/NAME.tar.gz
881
+** or: /tarball/VERSION/NAME.tar.gz
882
+** or: /tarball/TAG/VERSION/NAME.tar.gz
711883
**
712884
** Generate a compressed tarball for the check-in specified by VERSION.
713885
** The tarball is called NAME.tar.gz and has a top-level directory called
714
-** NAME.
886
+** NAME. If TAG is provided, then VERSION must hold TAG or else an error
887
+** is returned.
715888
**
716889
** The optional VERSION element defaults to "trunk" per the r= rules below.
717890
** All of the following URLs are equivalent:
718891
**
719892
** /tarball/release/xyz.tar.gz
@@ -745,10 +918,25 @@
745918
**
746919
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
747920
** comma-separated list of GLOB patterns, where each
748921
** pattern can optionally be quoted using ".." or '..'.
749922
** Any file matching both ex= and in= is excluded.
923
+**
924
+** Robot Defenses:
925
+**
926
+** * If "zip" appears in the robot-restrict setting, then robots are
927
+** not allowed to access this page. Suspected robots will be
928
+** presented with a captcha.
929
+**
930
+** * If "zipX" appears in the robot-restrict setting, then robots are
931
+** restricted in the same way as with "zip", but with exceptions.
932
+** If the check-in for which an archive is requested is a leaf check-in
933
+** and if the robot-zip-leaf setting is true, then the request is
934
+** allowed. Or if the check-in has a tag that matches any of the
935
+** GLOB patterns on the list in the robot-zip-tag setting, then the
936
+** request is allowed. Otherwise, the usual robot defenses are
937
+** activated.
750938
*/
751939
void tarball_page(void){
752940
int rid;
753941
char *zName, *zRid, *zKey;
754942
int nName, nRid;
@@ -760,10 +948,11 @@
760948
Blob tarball; /* Tarball accumulated here */
761949
const char *z;
762950
763951
login_check_credentials();
764952
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
953
+ if( robot_restrict("zip") ) return;
765954
fossil_nice_default();
766955
zName = fossil_strdup(PD("name",""));
767956
z = P("r");
768957
if( z==0 ) z = P("uuid");
769958
if( z==0 ) z = tar_uuid_from_name(&zName);
@@ -796,10 +985,11 @@
796985
if( rid==0 ){
797986
cgi_set_status(404, "Not Found");
798987
@ Not found
799988
return;
800989
}
990
+ if( robot_restrict_zip(rid) ) return;
801991
if( nRid==0 && nName>10 ) zName[10] = 0;
802992
803993
/* Compute a unique key for the cache entry based on query parameters */
804994
blob_init(&cacheKey, 0, 0);
805995
blob_appendf(&cacheKey, "/tarball/%z", rid_to_uuid(rid));
@@ -847,5 +1037,312 @@
8471037
g.zOpenRevision = 0;
8481038
blob_reset(&cacheKey);
8491039
cgi_set_content(&tarball);
8501040
cgi_set_content_type("application/x-compressed");
8511041
}
1042
+
1043
+/*
1044
+** This routine is called for each check-in on the /download page to
1045
+** construct the "extra" information after the description.
1046
+*/
1047
+void download_extra(
1048
+ Stmt *pQuery, /* Current row of the timeline query */
1049
+ int tmFlags, /* Flags to www_print_timeline() */
1050
+ const char *zThisUser, /* Suppress links to this user */
1051
+ const char *zThisTag /* Suppress links to this tag */
1052
+){
1053
+ const char *zType = db_column_text(pQuery, 7);
1054
+ assert( zType!=0 );
1055
+ if( zType[0]!='c' ){
1056
+ timeline_extra(pQuery, tmFlags, zThisUser, zThisTag);
1057
+ }else{
1058
+ int rid = db_column_int(pQuery, 0);
1059
+ const char *zUuid = db_column_text(pQuery, 1);
1060
+ char *zBrName = branch_of_rid(rid);
1061
+ char *zNm;
1062
+
1063
+ if( tmFlags & TIMELINE_COLUMNAR ){
1064
+ @ <nobr>check-in:&nbsp;\
1065
+ @ %z(href("%R/info/%!S",zUuid))<span class='timelineHash'>\
1066
+ @ %S(zUuid)</span></a></nobr><br>
1067
+ if( fossil_strcmp(zBrName,"trunk")!=0 ){
1068
+ @ <nobr>branch:&nbsp;\
1069
+ @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a></nobr><br>\
1070
+ }
1071
+ }else{
1072
+ if( (tmFlags & TIMELINE_CLASSIC)==0 ){
1073
+ @ check-in:&nbsp;%z(href("%R/info/%!S",zUuid))\
1074
+ @ <span class='timelineHash'>%S(zUuid)</span></a>
1075
+ }
1076
+ if( (tmFlags & TIMELINE_GRAPH)==0 && fossil_strcmp(zBrName,"trunk")!=0 ){
1077
+ @ branch:&nbsp;\
1078
+ @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a>
1079
+ }
1080
+ }
1081
+ zNm = archive_base_name(rid);
1082
+ @ %z(href("%R/tarball/%s.tar.gz",zNm))\
1083
+ @ <button>Tarball</button></a>
1084
+ @ %z(href("%R/zip/%s.zip",zNm))\
1085
+ @ <button>ZIP&nbsp;Archive</button></a>
1086
+ fossil_free(zBrName);
1087
+ fossil_free(zNm);
1088
+ }
1089
+}
1090
+
1091
+/*
1092
+** SETTING: suggested-downloads width=70 block-text
1093
+**
1094
+** This setting controls the suggested tarball/ZIP downloads on the
1095
+** [[/download]] page. The value is a TCL list. Each set of four items
1096
+** defines a set of check-ins to be added to the suggestion list.
1097
+** The items in each group are:
1098
+**
1099
+** | COUNT TAG MAX_AGE COMMENT
1100
+**
1101
+** COUNT is the number of check-ins to match, starting with the most
1102
+** recent and working bacwards in time. Check-ins match if they contain
1103
+** the tag TAG. If MAX_AGE is not an empty string, then it specifies
1104
+** the maximum age of any matching check-in. COMMENT is an optional
1105
+** comment for each match.
1106
+**
1107
+** The special value of "OPEN-LEAF" for TAG matches any check-in that
1108
+** is an open leaf.
1109
+**
1110
+** MAX_AGE is of the form "{AMT UNITS}" where AMT is a floating point
1111
+** value and UNITS is one of "seconds", "hours", "days", "weeks", "months",
1112
+** or "years". If MAX_AGE is an empty string then there is no age limit.
1113
+**
1114
+** If COMMENT is not an empty string, then it is an additional comment
1115
+** added to the output description of the suggested download. The idea of
1116
+** COMMENT is to explain to the reader why a check-in is a suggested
1117
+** download.
1118
+**
1119
+** Example:
1120
+**
1121
+** | 1 trunk {} {Latest Trunk Check-in}
1122
+** | 5 OPEN-LEAF {1 month} {Active Branch}
1123
+** | 999 release {1 year} {Official Release}
1124
+**
1125
+** The value causes the /download page to show the union of the most
1126
+** recent trunk check-in of any age, the five most recent
1127
+** open leaves within the past month, and essentially
1128
+** all releases within the past year. If the same check-in matches more
1129
+** than one rule, the COMMENT of the first match is used.
1130
+*/
1131
+
1132
+/*
1133
+** WEBPAGE: /download
1134
+**
1135
+** Show a special no-graph timeline of recent important check-ins with
1136
+** an opportunity to pull tarballs and ZIPs.
1137
+*/
1138
+void download_page(void){
1139
+ Stmt q; /* The actual timeline query */
1140
+ const char *zTarlistCfg; /* Configuration string */
1141
+ char **azItem; /* Decomposed elements of zTarlistCfg */
1142
+ int *anItem; /* Bytes in each term of azItem[] */
1143
+ int nItem; /* Number of terms in azItem[] */
1144
+ int i; /* Loop counter */
1145
+ int tmFlags; /* Timeline display flags */
1146
+ int n; /* Number of suggested downloads */
1147
+ double rNow; /* Current time. Julian day number */
1148
+ int bPlainTextCom; /* Use plain-text comments */
1149
+
1150
+ login_check_credentials();
1151
+ if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1152
+
1153
+ style_set_current_feature("timeline");
1154
+ style_header("Suggested Downloads");
1155
+
1156
+ zTarlistCfg = db_get("suggested-downloads","off");
1157
+ db_multi_exec(
1158
+ "CREATE TEMP TABLE tarlist(rid INTEGER PRIMARY KEY, com TEXT);"
1159
+ );
1160
+ rNow = db_double(0.0,"SELECT julianday()");
1161
+ if( !g.interp ) Th_FossilInit(0);
1162
+ Th_SplitList(g.interp, zTarlistCfg, (int)strlen(zTarlistCfg),
1163
+ &azItem, &anItem, &nItem);
1164
+ bPlainTextCom = db_get_boolean("timeline-plaintext",0);
1165
+ for(i=0; i<nItem-3; i+=4){
1166
+ int cnt; /* The number of instances of zLabel to use */
1167
+ char *zLabel; /* The label to match */
1168
+ double rStart; /* Starting time, julian day number */
1169
+ char *zComment = 0; /* Comment to apply */
1170
+ if( anItem[i]==1 && azItem[i][0]=='*' ){
1171
+ cnt = -1;
1172
+ }else if( anItem[i]<1 ){
1173
+ cnt = 0;
1174
+ }else{
1175
+ cnt = atoi(azItem[i]);
1176
+ }
1177
+ if( cnt==0 ) continue;
1178
+ zLabel = fossil_strndup(azItem[i+1],anItem[i+1]);
1179
+ if( anItem[i+2]==0 ){
1180
+ rStart = 0.0;
1181
+ }else{
1182
+ char *zMax = fossil_strndup(azItem[i+2], anItem[i+2]);
1183
+ double r = atof(zMax);
1184
+ if( strstr(zMax,"sec") ){
1185
+ rStart = rNow - r/86400.0;
1186
+ }else
1187
+ if( strstr(zMax,"hou") ){
1188
+ rStart = rNow - r/24.0;
1189
+ }else
1190
+ if( strstr(zMax,"da") ){
1191
+ rStart = rNow - r;
1192
+ }else
1193
+ if( strstr(zMax,"wee") ){
1194
+ rStart = rNow - r*7.0;
1195
+ }else
1196
+ if( strstr(zMax,"mon") ){
1197
+ rStart = rNow - r*30.44;
1198
+ }else
1199
+ if( strstr(zMax,"yea") ){
1200
+ rStart = rNow - r*365.24;
1201
+ }else
1202
+ { /* Default to seconds */
1203
+ rStart = rNow - r/86400.0;
1204
+ }
1205
+ }
1206
+ if( anItem[i+3]==0 ){
1207
+ zComment = fossil_strdup("");
1208
+ }else if( bPlainTextCom ){
1209
+ zComment = mprintf("** %.*s ** ", anItem[i+3], azItem[i+3]);
1210
+ }else{
1211
+ zComment = mprintf("<b>%.*s</b>\n<p>", anItem[i+3], azItem[i+3]);
1212
+ }
1213
+ if( fossil_strcmp("OPEN-LEAF",zLabel)==0 ){
1214
+ db_multi_exec(
1215
+ "INSERT OR IGNORE INTO tarlist(rid,com)"
1216
+ " SELECT leaf.rid, %Q FROM leaf, event"
1217
+ " WHERE event.objid=leaf.rid"
1218
+ " AND event.mtime>=%.6f"
1219
+ " AND NOT EXISTS(SELECT 1 FROM tagxref"
1220
+ " WHERE tagxref.rid=leaf.rid"
1221
+ " AND tagid=%d AND tagtype>0)"
1222
+ " ORDER BY event.mtime DESC LIMIT %d",
1223
+ zComment, rStart, TAG_CLOSED, cnt
1224
+ );
1225
+ }else{
1226
+ db_multi_exec(
1227
+ "WITH taglist(tid) AS"
1228
+ " (SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q')"
1229
+ "INSERT OR IGNORE INTO tarlist(rid,com)"
1230
+ " SELECT event.objid, %Q FROM event CROSS JOIN tagxref"
1231
+ " WHERE event.type='ci'"
1232
+ " AND event.mtime>=%.6f"
1233
+ " AND tagxref.tagid IN taglist"
1234
+ " AND tagtype>0"
1235
+ " AND tagxref.rid=event.objid"
1236
+ " ORDER BY event.mtime DESC LIMIT %d",
1237
+ zLabel, zComment, rStart, cnt
1238
+ );
1239
+ }
1240
+ fossil_free(zLabel);
1241
+ fossil_free(zComment);
1242
+ }
1243
+ Th_Free(g.interp, azItem);
1244
+
1245
+ n = db_int(0, "SELECT count(*) FROM tarlist");
1246
+ if( n==0 ){
1247
+ @ <h2>No tarball/ZIP suggestions are available at this time</h2>
1248
+ }else{
1249
+ @ <h2>%d(n) Tarball/ZIP Download Suggestion%s(n>1?"s":""):</h2>
1250
+ db_prepare(&q,
1251
+ "WITH matches AS (%s AND blob.rid IN (SELECT rid FROM tarlist))\n"
1252
+ "SELECT blobRid, uuid, timestamp,"
1253
+ " com||comment,"
1254
+ " user, leaf, bgColor, eventType, tags, tagid, brief, mtime"
1255
+ " FROM matches JOIN tarlist ON tarlist.rid=blobRid"
1256
+ " ORDER BY matches.mtime DESC",
1257
+ timeline_query_for_www()
1258
+ );
1259
+
1260
+ tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
1261
+ | TIMELINE_BRCOLOR;
1262
+ www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, download_extra);
1263
+ db_finalize(&q);
1264
+ }
1265
+ if( g.perm.Clone ){
1266
+ char *zNm = fossil_strdup(db_get("project-name","clone"));
1267
+ sanitize_name(zNm);
1268
+ @ <hr>
1269
+ @ <h2>You Can Clone This Repository</h2>
1270
+ @
1271
+ @ <p>Clone this repository by running a command similar to the following:
1272
+ @ <blockquote><pre>
1273
+ @ fossil clone %s(g.zBaseURL) %h(zNm).fossil
1274
+ @ </pre></blockquote>
1275
+ @ <p>A clone gives you local access to all historical content.
1276
+ @ Cloning is a bandwidth- and CPU-efficient alternative to extracting
1277
+ @ multiple tarballs and ZIPs.
1278
+ @ Do a web search for "fossil clone" or similar to find additional
1279
+ @ information about using a cloned Fossil repository. Or ask your
1280
+ @ favorite AI how to extract content from a Fossil clone.
1281
+ fossil_free(zNm);
1282
+ }
1283
+
1284
+ style_finish_page();
1285
+}
1286
+
1287
+/*
1288
+** WEBPAGE: rchvdwnld
1289
+**
1290
+** Short for "archive download". This page should have a single name=
1291
+** query parameter that is a check-in hash. It present a menu of possible
1292
+** download options for that check-in, including tarball, ZIP, or SQLAR.
1293
+**
1294
+** This is a utility page. The /dir and /tree pages sometimes have a
1295
+** "Download" option in their submenu which redirects here. Those pages
1296
+** used to have separate "Tarball" and "ZIP" submenu entries, but as
1297
+** submenu entries appear in alphabetical order, that caused the two
1298
+** submenu entries to be separated from one another, which is distracting.
1299
+*/
1300
+void rchvdwnld_page(void){
1301
+ const char *zUuid;
1302
+ char *zBase;
1303
+ int nUuid;
1304
+ int rid;
1305
+ login_check_credentials();
1306
+ if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1307
+ if( robot_restrict("zip") || robot_restrict("download") ) return;
1308
+
1309
+ zUuid = P("name");
1310
+ if( zUuid==0
1311
+ || (nUuid = (int)strlen(zUuid))<6
1312
+ || !validate16(zUuid,-1)
1313
+ || (rid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid))==0
1314
+ || !db_exists("SELECT 1 from event WHERE type='ci' AND objid=%d",rid)
1315
+ ){
1316
+ fossil_redirect_home();
1317
+ }
1318
+ zUuid = db_text(zUuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
1319
+ style_header("Downloads For Check-in %!S", zUuid);
1320
+ zBase = archive_base_name(rid);
1321
+ @ <div class="section accordion">Downloads for check-in \
1322
+ @ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div>
1323
+ @ <div class="accordion_panel">
1324
+ @ <table class="label-value">
1325
+ @ <tr>
1326
+ @ <th>Tarball:</th>
1327
+ @ <td>%z(href("%R/tarball/%s.tar.gz",zBase))\
1328
+ @ %s(g.zBaseURL)/tarball/%s(zBase).tar.gz</a></td>
1329
+ @ </tr>
1330
+ @
1331
+ @ <tr>
1332
+ @ <th>ZIP:</th>
1333
+ @ <td>%z(href("%R/zip/%s.zip",zBase))\
1334
+ @ %s(g.zBaseURL)/zip/%s(zBase).zip</a></td>
1335
+ @ </tr>
1336
+ @
1337
+ @ <tr>
1338
+ @ <th>SQLAR:</th>
1339
+ @ <td>%z(href("%R/sqlar/%s.sqlar",zBase))\
1340
+ @ %s(g.zBaseURL)/sqlar/%s(zBase).sqlar</a></td>
1341
+ @ </tr>
1342
+ @ </table></div>
1343
+ fossil_free(zBase);
1344
+ @ <div class="section accordion">Context</div><div class="accordion_panel">
1345
+ render_checkin_context(rid, 0, 0, 0);
1346
+ @ </div>
1347
+ style_finish_page();
1348
+}
8521349
--- src/tar.c
+++ src/tar.c
@@ -31,10 +31,65 @@
31 char *zPrevDir; /* Name of directory for previous entry */
32 int nPrevDirAlloc; /* size of zPrevDir */
33 Blob pax; /* PAX data */
34 } tball;
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
37 /*
38 ** field lengths of 'ustar' name and prefix fields.
39 */
40 #define USTAR_NAME_LEN 100
@@ -653,19 +708,11 @@
653 if( fossil_strcmp("/dev/null",zOut)==0 || fossil_strcmp("",zOut)==0 ){
654 zOut = 0;
655 }
656
657 if( zName==0 ){
658 zName = db_text("default-name",
659 "SELECT replace(%Q,' ','_') "
660 " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
661 " || substr(blob.uuid, 1, 10)"
662 " FROM event, blob"
663 " WHERE event.objid=%d"
664 " AND blob.rid=%d",
665 db_get("project-name", "unnamed"), rid, rid
666 );
667 }
668 tarball_of_checkin(rid, zOut ? &tarball : 0,
669 zName, pInclude, pExclude, listFlag);
670 glob_free(pInclude);
671 glob_free(pExclude);
@@ -675,45 +722,171 @@
675 blob_reset(&tarball);
676 }
677 }
678
679 /*
680 ** Check to see if the input string is of the form:
681 **
682 ** check-in-name/filename.ext
683 **
684 ** In other words, check to see if the input contains a single '/'
685 ** character that separates a valid check-in name from a filename.
686 **
687 ** If the condition is true, return the check-in name and set the
688 ** input string to be the filename.
689 **
690 ** If the condition is false, return NULL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
691 */
692 char *tar_uuid_from_name(char **pzName){
693 char *zName = *pzName;
694 int i, n;
695 for(i=n=0; zName[i]; i++){
 
 
 
696 if( zName[i]=='/' ){
697 if( n==0 ) n = i;
698 else return 0;
699 }
700 }
701 if( n==0 ) return 0;
702 if( zName[n+1]==0 ) return 0;
703 zName[n] = 0;
704 *pzName = fossil_strdup(&zName[n+1]);
705 return zName;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706 }
707
708 /*
709 ** WEBPAGE: tarball
710 ** URL: /tarball/[VERSION/]NAME.tar.gz
 
 
711 **
712 ** Generate a compressed tarball for the check-in specified by VERSION.
713 ** The tarball is called NAME.tar.gz and has a top-level directory called
714 ** NAME.
 
715 **
716 ** The optional VERSION element defaults to "trunk" per the r= rules below.
717 ** All of the following URLs are equivalent:
718 **
719 ** /tarball/release/xyz.tar.gz
@@ -745,10 +918,25 @@
745 **
746 ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
747 ** comma-separated list of GLOB patterns, where each
748 ** pattern can optionally be quoted using ".." or '..'.
749 ** Any file matching both ex= and in= is excluded.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
750 */
751 void tarball_page(void){
752 int rid;
753 char *zName, *zRid, *zKey;
754 int nName, nRid;
@@ -760,10 +948,11 @@
760 Blob tarball; /* Tarball accumulated here */
761 const char *z;
762
763 login_check_credentials();
764 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
 
765 fossil_nice_default();
766 zName = fossil_strdup(PD("name",""));
767 z = P("r");
768 if( z==0 ) z = P("uuid");
769 if( z==0 ) z = tar_uuid_from_name(&zName);
@@ -796,10 +985,11 @@
796 if( rid==0 ){
797 cgi_set_status(404, "Not Found");
798 @ Not found
799 return;
800 }
 
801 if( nRid==0 && nName>10 ) zName[10] = 0;
802
803 /* Compute a unique key for the cache entry based on query parameters */
804 blob_init(&cacheKey, 0, 0);
805 blob_appendf(&cacheKey, "/tarball/%z", rid_to_uuid(rid));
@@ -847,5 +1037,312 @@
847 g.zOpenRevision = 0;
848 blob_reset(&cacheKey);
849 cgi_set_content(&tarball);
850 cgi_set_content_type("application/x-compressed");
851 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852
--- src/tar.c
+++ src/tar.c
@@ -31,10 +31,65 @@
31 char *zPrevDir; /* Name of directory for previous entry */
32 int nPrevDirAlloc; /* size of zPrevDir */
33 Blob pax; /* PAX data */
34 } tball;
35
36 /*
37 ** Convert a string so that it contains only lower-case ASCII, digits,
38 ** "_" and "-". Changes are made in-place.
39 */
40 static void sanitize_name(char *zName){
41 int i;
42 char c;
43 for(i=0; (c = zName[i])!=0; i++){
44 if( fossil_isupper(c) ){
45 zName[i] = fossil_tolower(c);
46 }else if( !fossil_isalnum(c) && c!='_' && c!='-' ){
47 if( c<=0x7f ){
48 zName[i] = '_';
49 }else{
50 /* 123456789 123456789 123456 */
51 zName[i] = "abcdefghijklmnopqrstuvwxyz"[(unsigned)c%26];
52 }
53 }
54 }
55 }
56
57 /*
58 ** Compute a sensible base-name for an archive file (tarball, ZIP, or SQLAR)
59 ** based on the rid of the check-in contained in that file.
60 **
61 ** PROJECTNAME-DATETIME-HASHPREFIX
62 **
63 ** So that the name will be safe to use as a URL or a filename on any system,
64 ** the name is only allowed to contain lower-case ASCII alphabetics,
65 ** digits, '_' and '-'. Upper-case ASCII is converted to lower-case. All
66 ** other bytes are mapped into a lower-case alphabetic.
67 **
68 ** The value returned is obtained from mprintf() or fossil_strdup() and should
69 ** be released by the caller using fossil_free().
70 */
71 char *archive_base_name(int rid){
72 char *zPrefix;
73 char *zName;
74 zPrefix = db_get("short-project-name",0);
75 if( zPrefix==0 || zPrefix[0]==0 ){
76 zPrefix = db_get("project-name","unnamed");
77 }
78 zName = db_text(0,
79 "SELECT %Q||"
80 " strftime('-%%Y%%m%%d%%H%%M%%S-',event.mtime)||"
81 " substr(blob.uuid,1,10)"
82 " FROM blob, event LEFT JOIN config"
83 " WHERE blob.rid=%d"
84 " AND event.objid=%d"
85 " AND config.name='project-name'",
86 zPrefix, rid, rid);
87 fossil_free(zPrefix);
88 sanitize_name(zName);
89 return zName;
90 }
91
92 /*
93 ** field lengths of 'ustar' name and prefix fields.
94 */
95 #define USTAR_NAME_LEN 100
@@ -653,19 +708,11 @@
708 if( fossil_strcmp("/dev/null",zOut)==0 || fossil_strcmp("",zOut)==0 ){
709 zOut = 0;
710 }
711
712 if( zName==0 ){
713 zName = archive_base_name(rid);
 
 
 
 
 
 
 
 
714 }
715 tarball_of_checkin(rid, zOut ? &tarball : 0,
716 zName, pInclude, pExclude, listFlag);
717 glob_free(pInclude);
718 glob_free(pExclude);
@@ -675,45 +722,171 @@
722 blob_reset(&tarball);
723 }
724 }
725
726 /*
727 ** This is a helper routine for tar_uuid_from_name(). It handles
728 ** the case where *pzName contains no "/" character. Check for
729 ** format (3). Return the hash if the name matches format (3),
730 ** or return NULL if it does not.
731 */
732 static char *format_three_parser(const char *zName){
733 int iDot = 0; /* Index in zName[] of the first '.' */
734 int iDash1 = 0; /* Index in zName[] of the '-' before the timestamp */
735 int iDash2 = 0; /* Index in zName[] of the '-' between timestamp and hash */
736 int nHash; /* Size of the hash */
737 char *zHash; /* A copy of the hash value */
738 char *zDate; /* Copy of the timestamp */
739 char *zUuid; /* Final result */
740 int i; /* Loop query */
741 Stmt q; /* Query to verify that hash and timestamp agree */
742
743 for(i=0; zName[i]; i++){
744 char c = zName[i];
745 if( c=='.' ){ iDot = i; break; }
746 if( c=='-' ){ iDash1 = iDash2; iDash2 = i; }
747 if( !fossil_isalnum(c) && c!='_' && c!='-' ){ break; }
748 }
749 if( iDot==0 ) return 0;
750 if( iDash1==0 ) return 0;
751 nHash = iDot - iDash2 - 1;
752 if( nHash<8 ) return 0; /* HASH value too short */
753 if( (iDash2 - iDash1)!=15 ) return 0; /* Wrong timestamp size */
754 zHash = fossil_strndup(&zName[iDash2+1], nHash);
755 zDate = fossil_strndup(&zName[iDash1+1], 14);
756 db_prepare(&q,
757 "SELECT blob.uuid"
758 " FROM blob JOIN event ON event.objid=blob.rid"
759 " WHERE blob.uuid GLOB '%q*'"
760 " AND strftime('%%Y%%m%%d%%H%%M%%S',event.mtime)='%q'",
761 zHash, zDate
762 );
763 fossil_free(zHash);
764 fossil_free(zDate);
765 if( db_step(&q)==SQLITE_ROW ){
766 zUuid = fossil_strdup(db_column_text(&q,0));
767 }else{
768 zUuid = 0;
769 }
770 db_finalize(&q);
771 return zUuid;
772 }
773
774 /*
775 ** Check to see if the input string is of one of the following
776 ** two the forms:
777 **
778 ** check-in-name/filename.ext (1)
779 ** tag-name/check-in-name/filename.ext (2)
780 ** project-datetime-hash.ext (3)
781 **
782 ** In other words, check to see if the input string contains either
783 ** a check-in name or a tag-name and a check-in name separated by
784 ** a slash. There must be between 0 or 2 "/" characters. In the
785 ** second form, tag-name must be an individual tag (not a branch-tag)
786 ** that is found on the check-in identified by the check-in-name.
787 **
788 ** If the condition is true, then:
789 **
790 ** * Make *pzName point to the filename suffix only
791 ** * return a copy of the check-in name in memory from mprintf().
792 **
793 ** If the condition is false, leave *pzName unchanged and return either
794 ** NULL or an empty string. Normally NULL is returned, however an
795 ** empty string is returned for format (2) if check-in-name does not
796 ** match tag-name.
797 **
798 ** Format (2) is specifically designed to allow URLs like this:
799 **
800 ** /tarball/release/UUID/PROJECT.tar.gz
801 **
802 ** Such URLs will pass through most anti-robot filters because of the
803 ** "/tarball/release" prefix will match the suggested "robot-exception"
804 ** pattern and can still refer to an historic release rather than just
805 ** the most recent release.
806 **
807 ** Format (3) is designed to allow URLs like this:
808 **
809 ** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz
810 **
811 ** In other words, filename itself contains sufficient information to
812 ** uniquely identify the check-in, including a timestamp of the form
813 ** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp
814 ** and hash must immediately preceed the first "." in the name.
815 */
816 char *tar_uuid_from_name(char **pzName){
817 char *zName = *pzName; /* Original input */
818 int n1 = 0; /* Bytes in first prefix (tag-name) */
819 int n2 = 0; /* Bytes in second prefix (check-in-name) */
820 int n = 0; /* max(n1,n2) */
821 int i; /* Loop counter */
822 for(i=n1=n2=0; zName[i]; i++){
823 if( zName[i]=='/' ){
824 if( n1==0 ){
825 n = n1 = i;
826 }else if( n2==0 ){
827 n = n2 = i;
828 }else{
829 return 0; /* More than two "/" characters seen */
830 }
831 }
832 }
833 if( n1==0 ){
834 /* Check for format (3) */
835 return format_three_parser(*pzName);
836 }
837 if( zName[n+1]==0 ){
838 return 0; /* No filename suffix */
839 }
840 if( n2==0 ){
841 /* Format (1): check-in name only. The check-in-name is not verified */
842 zName[n1] = 0;
843 *pzName = fossil_strdup(&zName[n1+1]);
844 return zName;
845 }else if( n2>n1+1 ){
846 /* Format (2): tag-name/check-in-name. Verify that check-in-name is real
847 ** and that the check-in has the tag named by tag-name.
848 */
849 char *zCkin = mprintf("%.*s", n2-n1-1, &zName[n1+1]);
850 char *zTag;
851 int rid = symbolic_name_to_rid(zCkin,"ci");
852 int hasTag;
853 if( rid<=0 ){
854 fossil_free(zCkin);
855 return fossil_strdup("");
856 }
857 zTag = mprintf("%.*s", n1, zName);
858 hasTag = db_exists(
859 "SELECT 1 FROM tagxref, tag"
860 " WHERE tagxref.rid=%d"
861 " AND tag.tagid=tagxref.tagid"
862 " AND tagxref.tagtype=1"
863 " AND tag.tagname='sym-%q'",
864 rid, zTag
865 );
866 fossil_free(zTag);
867 if( !hasTag ){
868 fossil_free(zCkin);
869 return fossil_strdup("");
870 }
871 *pzName = fossil_strdup(&zName[n2+1]);
872 return zCkin;
873 }else{
874 return 0;
875 }
876 }
877
878 /*
879 ** WEBPAGE: tarball
880 ** URL: /tarball/NAME.tar.gz
881 ** or: /tarball/VERSION/NAME.tar.gz
882 ** or: /tarball/TAG/VERSION/NAME.tar.gz
883 **
884 ** Generate a compressed tarball for the check-in specified by VERSION.
885 ** The tarball is called NAME.tar.gz and has a top-level directory called
886 ** NAME. If TAG is provided, then VERSION must hold TAG or else an error
887 ** is returned.
888 **
889 ** The optional VERSION element defaults to "trunk" per the r= rules below.
890 ** All of the following URLs are equivalent:
891 **
892 ** /tarball/release/xyz.tar.gz
@@ -745,10 +918,25 @@
918 **
919 ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
920 ** comma-separated list of GLOB patterns, where each
921 ** pattern can optionally be quoted using ".." or '..'.
922 ** Any file matching both ex= and in= is excluded.
923 **
924 ** Robot Defenses:
925 **
926 ** * If "zip" appears in the robot-restrict setting, then robots are
927 ** not allowed to access this page. Suspected robots will be
928 ** presented with a captcha.
929 **
930 ** * If "zipX" appears in the robot-restrict setting, then robots are
931 ** restricted in the same way as with "zip", but with exceptions.
932 ** If the check-in for which an archive is requested is a leaf check-in
933 ** and if the robot-zip-leaf setting is true, then the request is
934 ** allowed. Or if the check-in has a tag that matches any of the
935 ** GLOB patterns on the list in the robot-zip-tag setting, then the
936 ** request is allowed. Otherwise, the usual robot defenses are
937 ** activated.
938 */
939 void tarball_page(void){
940 int rid;
941 char *zName, *zRid, *zKey;
942 int nName, nRid;
@@ -760,10 +948,11 @@
948 Blob tarball; /* Tarball accumulated here */
949 const char *z;
950
951 login_check_credentials();
952 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
953 if( robot_restrict("zip") ) return;
954 fossil_nice_default();
955 zName = fossil_strdup(PD("name",""));
956 z = P("r");
957 if( z==0 ) z = P("uuid");
958 if( z==0 ) z = tar_uuid_from_name(&zName);
@@ -796,10 +985,11 @@
985 if( rid==0 ){
986 cgi_set_status(404, "Not Found");
987 @ Not found
988 return;
989 }
990 if( robot_restrict_zip(rid) ) return;
991 if( nRid==0 && nName>10 ) zName[10] = 0;
992
993 /* Compute a unique key for the cache entry based on query parameters */
994 blob_init(&cacheKey, 0, 0);
995 blob_appendf(&cacheKey, "/tarball/%z", rid_to_uuid(rid));
@@ -847,5 +1037,312 @@
1037 g.zOpenRevision = 0;
1038 blob_reset(&cacheKey);
1039 cgi_set_content(&tarball);
1040 cgi_set_content_type("application/x-compressed");
1041 }
1042
1043 /*
1044 ** This routine is called for each check-in on the /download page to
1045 ** construct the "extra" information after the description.
1046 */
1047 void download_extra(
1048 Stmt *pQuery, /* Current row of the timeline query */
1049 int tmFlags, /* Flags to www_print_timeline() */
1050 const char *zThisUser, /* Suppress links to this user */
1051 const char *zThisTag /* Suppress links to this tag */
1052 ){
1053 const char *zType = db_column_text(pQuery, 7);
1054 assert( zType!=0 );
1055 if( zType[0]!='c' ){
1056 timeline_extra(pQuery, tmFlags, zThisUser, zThisTag);
1057 }else{
1058 int rid = db_column_int(pQuery, 0);
1059 const char *zUuid = db_column_text(pQuery, 1);
1060 char *zBrName = branch_of_rid(rid);
1061 char *zNm;
1062
1063 if( tmFlags & TIMELINE_COLUMNAR ){
1064 @ <nobr>check-in:&nbsp;\
1065 @ %z(href("%R/info/%!S",zUuid))<span class='timelineHash'>\
1066 @ %S(zUuid)</span></a></nobr><br>
1067 if( fossil_strcmp(zBrName,"trunk")!=0 ){
1068 @ <nobr>branch:&nbsp;\
1069 @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a></nobr><br>\
1070 }
1071 }else{
1072 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
1073 @ check-in:&nbsp;%z(href("%R/info/%!S",zUuid))\
1074 @ <span class='timelineHash'>%S(zUuid)</span></a>
1075 }
1076 if( (tmFlags & TIMELINE_GRAPH)==0 && fossil_strcmp(zBrName,"trunk")!=0 ){
1077 @ branch:&nbsp;\
1078 @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a>
1079 }
1080 }
1081 zNm = archive_base_name(rid);
1082 @ %z(href("%R/tarball/%s.tar.gz",zNm))\
1083 @ <button>Tarball</button></a>
1084 @ %z(href("%R/zip/%s.zip",zNm))\
1085 @ <button>ZIP&nbsp;Archive</button></a>
1086 fossil_free(zBrName);
1087 fossil_free(zNm);
1088 }
1089 }
1090
1091 /*
1092 ** SETTING: suggested-downloads width=70 block-text
1093 **
1094 ** This setting controls the suggested tarball/ZIP downloads on the
1095 ** [[/download]] page. The value is a TCL list. Each set of four items
1096 ** defines a set of check-ins to be added to the suggestion list.
1097 ** The items in each group are:
1098 **
1099 ** | COUNT TAG MAX_AGE COMMENT
1100 **
1101 ** COUNT is the number of check-ins to match, starting with the most
1102 ** recent and working bacwards in time. Check-ins match if they contain
1103 ** the tag TAG. If MAX_AGE is not an empty string, then it specifies
1104 ** the maximum age of any matching check-in. COMMENT is an optional
1105 ** comment for each match.
1106 **
1107 ** The special value of "OPEN-LEAF" for TAG matches any check-in that
1108 ** is an open leaf.
1109 **
1110 ** MAX_AGE is of the form "{AMT UNITS}" where AMT is a floating point
1111 ** value and UNITS is one of "seconds", "hours", "days", "weeks", "months",
1112 ** or "years". If MAX_AGE is an empty string then there is no age limit.
1113 **
1114 ** If COMMENT is not an empty string, then it is an additional comment
1115 ** added to the output description of the suggested download. The idea of
1116 ** COMMENT is to explain to the reader why a check-in is a suggested
1117 ** download.
1118 **
1119 ** Example:
1120 **
1121 ** | 1 trunk {} {Latest Trunk Check-in}
1122 ** | 5 OPEN-LEAF {1 month} {Active Branch}
1123 ** | 999 release {1 year} {Official Release}
1124 **
1125 ** The value causes the /download page to show the union of the most
1126 ** recent trunk check-in of any age, the five most recent
1127 ** open leaves within the past month, and essentially
1128 ** all releases within the past year. If the same check-in matches more
1129 ** than one rule, the COMMENT of the first match is used.
1130 */
1131
1132 /*
1133 ** WEBPAGE: /download
1134 **
1135 ** Show a special no-graph timeline of recent important check-ins with
1136 ** an opportunity to pull tarballs and ZIPs.
1137 */
1138 void download_page(void){
1139 Stmt q; /* The actual timeline query */
1140 const char *zTarlistCfg; /* Configuration string */
1141 char **azItem; /* Decomposed elements of zTarlistCfg */
1142 int *anItem; /* Bytes in each term of azItem[] */
1143 int nItem; /* Number of terms in azItem[] */
1144 int i; /* Loop counter */
1145 int tmFlags; /* Timeline display flags */
1146 int n; /* Number of suggested downloads */
1147 double rNow; /* Current time. Julian day number */
1148 int bPlainTextCom; /* Use plain-text comments */
1149
1150 login_check_credentials();
1151 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1152
1153 style_set_current_feature("timeline");
1154 style_header("Suggested Downloads");
1155
1156 zTarlistCfg = db_get("suggested-downloads","off");
1157 db_multi_exec(
1158 "CREATE TEMP TABLE tarlist(rid INTEGER PRIMARY KEY, com TEXT);"
1159 );
1160 rNow = db_double(0.0,"SELECT julianday()");
1161 if( !g.interp ) Th_FossilInit(0);
1162 Th_SplitList(g.interp, zTarlistCfg, (int)strlen(zTarlistCfg),
1163 &azItem, &anItem, &nItem);
1164 bPlainTextCom = db_get_boolean("timeline-plaintext",0);
1165 for(i=0; i<nItem-3; i+=4){
1166 int cnt; /* The number of instances of zLabel to use */
1167 char *zLabel; /* The label to match */
1168 double rStart; /* Starting time, julian day number */
1169 char *zComment = 0; /* Comment to apply */
1170 if( anItem[i]==1 && azItem[i][0]=='*' ){
1171 cnt = -1;
1172 }else if( anItem[i]<1 ){
1173 cnt = 0;
1174 }else{
1175 cnt = atoi(azItem[i]);
1176 }
1177 if( cnt==0 ) continue;
1178 zLabel = fossil_strndup(azItem[i+1],anItem[i+1]);
1179 if( anItem[i+2]==0 ){
1180 rStart = 0.0;
1181 }else{
1182 char *zMax = fossil_strndup(azItem[i+2], anItem[i+2]);
1183 double r = atof(zMax);
1184 if( strstr(zMax,"sec") ){
1185 rStart = rNow - r/86400.0;
1186 }else
1187 if( strstr(zMax,"hou") ){
1188 rStart = rNow - r/24.0;
1189 }else
1190 if( strstr(zMax,"da") ){
1191 rStart = rNow - r;
1192 }else
1193 if( strstr(zMax,"wee") ){
1194 rStart = rNow - r*7.0;
1195 }else
1196 if( strstr(zMax,"mon") ){
1197 rStart = rNow - r*30.44;
1198 }else
1199 if( strstr(zMax,"yea") ){
1200 rStart = rNow - r*365.24;
1201 }else
1202 { /* Default to seconds */
1203 rStart = rNow - r/86400.0;
1204 }
1205 }
1206 if( anItem[i+3]==0 ){
1207 zComment = fossil_strdup("");
1208 }else if( bPlainTextCom ){
1209 zComment = mprintf("** %.*s ** ", anItem[i+3], azItem[i+3]);
1210 }else{
1211 zComment = mprintf("<b>%.*s</b>\n<p>", anItem[i+3], azItem[i+3]);
1212 }
1213 if( fossil_strcmp("OPEN-LEAF",zLabel)==0 ){
1214 db_multi_exec(
1215 "INSERT OR IGNORE INTO tarlist(rid,com)"
1216 " SELECT leaf.rid, %Q FROM leaf, event"
1217 " WHERE event.objid=leaf.rid"
1218 " AND event.mtime>=%.6f"
1219 " AND NOT EXISTS(SELECT 1 FROM tagxref"
1220 " WHERE tagxref.rid=leaf.rid"
1221 " AND tagid=%d AND tagtype>0)"
1222 " ORDER BY event.mtime DESC LIMIT %d",
1223 zComment, rStart, TAG_CLOSED, cnt
1224 );
1225 }else{
1226 db_multi_exec(
1227 "WITH taglist(tid) AS"
1228 " (SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q')"
1229 "INSERT OR IGNORE INTO tarlist(rid,com)"
1230 " SELECT event.objid, %Q FROM event CROSS JOIN tagxref"
1231 " WHERE event.type='ci'"
1232 " AND event.mtime>=%.6f"
1233 " AND tagxref.tagid IN taglist"
1234 " AND tagtype>0"
1235 " AND tagxref.rid=event.objid"
1236 " ORDER BY event.mtime DESC LIMIT %d",
1237 zLabel, zComment, rStart, cnt
1238 );
1239 }
1240 fossil_free(zLabel);
1241 fossil_free(zComment);
1242 }
1243 Th_Free(g.interp, azItem);
1244
1245 n = db_int(0, "SELECT count(*) FROM tarlist");
1246 if( n==0 ){
1247 @ <h2>No tarball/ZIP suggestions are available at this time</h2>
1248 }else{
1249 @ <h2>%d(n) Tarball/ZIP Download Suggestion%s(n>1?"s":""):</h2>
1250 db_prepare(&q,
1251 "WITH matches AS (%s AND blob.rid IN (SELECT rid FROM tarlist))\n"
1252 "SELECT blobRid, uuid, timestamp,"
1253 " com||comment,"
1254 " user, leaf, bgColor, eventType, tags, tagid, brief, mtime"
1255 " FROM matches JOIN tarlist ON tarlist.rid=blobRid"
1256 " ORDER BY matches.mtime DESC",
1257 timeline_query_for_www()
1258 );
1259
1260 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
1261 | TIMELINE_BRCOLOR;
1262 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, download_extra);
1263 db_finalize(&q);
1264 }
1265 if( g.perm.Clone ){
1266 char *zNm = fossil_strdup(db_get("project-name","clone"));
1267 sanitize_name(zNm);
1268 @ <hr>
1269 @ <h2>You Can Clone This Repository</h2>
1270 @
1271 @ <p>Clone this repository by running a command similar to the following:
1272 @ <blockquote><pre>
1273 @ fossil clone %s(g.zBaseURL) %h(zNm).fossil
1274 @ </pre></blockquote>
1275 @ <p>A clone gives you local access to all historical content.
1276 @ Cloning is a bandwidth- and CPU-efficient alternative to extracting
1277 @ multiple tarballs and ZIPs.
1278 @ Do a web search for "fossil clone" or similar to find additional
1279 @ information about using a cloned Fossil repository. Or ask your
1280 @ favorite AI how to extract content from a Fossil clone.
1281 fossil_free(zNm);
1282 }
1283
1284 style_finish_page();
1285 }
1286
1287 /*
1288 ** WEBPAGE: rchvdwnld
1289 **
1290 ** Short for "archive download". This page should have a single name=
1291 ** query parameter that is a check-in hash. It present a menu of possible
1292 ** download options for that check-in, including tarball, ZIP, or SQLAR.
1293 **
1294 ** This is a utility page. The /dir and /tree pages sometimes have a
1295 ** "Download" option in their submenu which redirects here. Those pages
1296 ** used to have separate "Tarball" and "ZIP" submenu entries, but as
1297 ** submenu entries appear in alphabetical order, that caused the two
1298 ** submenu entries to be separated from one another, which is distracting.
1299 */
1300 void rchvdwnld_page(void){
1301 const char *zUuid;
1302 char *zBase;
1303 int nUuid;
1304 int rid;
1305 login_check_credentials();
1306 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1307 if( robot_restrict("zip") || robot_restrict("download") ) return;
1308
1309 zUuid = P("name");
1310 if( zUuid==0
1311 || (nUuid = (int)strlen(zUuid))<6
1312 || !validate16(zUuid,-1)
1313 || (rid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid))==0
1314 || !db_exists("SELECT 1 from event WHERE type='ci' AND objid=%d",rid)
1315 ){
1316 fossil_redirect_home();
1317 }
1318 zUuid = db_text(zUuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
1319 style_header("Downloads For Check-in %!S", zUuid);
1320 zBase = archive_base_name(rid);
1321 @ <div class="section accordion">Downloads for check-in \
1322 @ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div>
1323 @ <div class="accordion_panel">
1324 @ <table class="label-value">
1325 @ <tr>
1326 @ <th>Tarball:</th>
1327 @ <td>%z(href("%R/tarball/%s.tar.gz",zBase))\
1328 @ %s(g.zBaseURL)/tarball/%s(zBase).tar.gz</a></td>
1329 @ </tr>
1330 @
1331 @ <tr>
1332 @ <th>ZIP:</th>
1333 @ <td>%z(href("%R/zip/%s.zip",zBase))\
1334 @ %s(g.zBaseURL)/zip/%s(zBase).zip</a></td>
1335 @ </tr>
1336 @
1337 @ <tr>
1338 @ <th>SQLAR:</th>
1339 @ <td>%z(href("%R/sqlar/%s.sqlar",zBase))\
1340 @ %s(g.zBaseURL)/sqlar/%s(zBase).sqlar</a></td>
1341 @ </tr>
1342 @ </table></div>
1343 fossil_free(zBase);
1344 @ <div class="section accordion">Context</div><div class="accordion_panel">
1345 render_checkin_context(rid, 0, 0, 0);
1346 @ </div>
1347 style_finish_page();
1348 }
1349
+1 -1
--- src/th_lang.c
+++ src/th_lang.c
@@ -956,11 +956,11 @@
956956
**
957957
*/
958958
static int string_match_command(
959959
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
960960
){
961
- extern char *fossil_strndup(const char*,int);
961
+ extern char *fossil_strndup(const char*,ssize_t);
962962
extern void fossil_free(void*);
963963
char *zPat, *zStr;
964964
int rc;
965965
if( argc!=4 ){
966966
return Th_WrongNumArgs(interp, "string match pattern string");
967967
--- src/th_lang.c
+++ src/th_lang.c
@@ -956,11 +956,11 @@
956 **
957 */
958 static int string_match_command(
959 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
960 ){
961 extern char *fossil_strndup(const char*,int);
962 extern void fossil_free(void*);
963 char *zPat, *zStr;
964 int rc;
965 if( argc!=4 ){
966 return Th_WrongNumArgs(interp, "string match pattern string");
967
--- src/th_lang.c
+++ src/th_lang.c
@@ -956,11 +956,11 @@
956 **
957 */
958 static int string_match_command(
959 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
960 ){
961 extern char *fossil_strndup(const char*,ssize_t);
962 extern void fossil_free(void*);
963 char *zPat, *zStr;
964 int rc;
965 if( argc!=4 ){
966 return Th_WrongNumArgs(interp, "string match pattern string");
967
+3 -3
--- src/th_main.c
+++ src/th_main.c
@@ -2145,11 +2145,11 @@
21452145
}
21462146
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
21472147
if( nArg+2!=argc ){
21482148
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
21492149
}
2150
- zErr = re_compile(&pRe, argv[nArg], noCase);
2150
+ zErr = fossil_re_compile(&pRe, argv[nArg], noCase);
21512151
if( !zErr ){
21522152
Th_SetResultInt(interp, re_match(pRe,
21532153
(const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
21542154
rc = TH_OK;
21552155
}else{
@@ -2202,11 +2202,11 @@
22022202
Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
22032203
return TH_ERROR;
22042204
}
22052205
zRegexp = db_get("th1-uri-regexp", 0);
22062206
if( zRegexp && zRegexp[0] ){
2207
- const char *zErr = re_compile(&pRe, zRegexp, 0);
2207
+ const char *zErr = fossil_re_compile(&pRe, zRegexp, 0);
22082208
if( zErr ){
22092209
Th_SetResult(interp, zErr, -1);
22102210
return TH_ERROR;
22112211
}
22122212
}
@@ -2965,11 +2965,11 @@
29652965
if( rc!=TH_OK ) break;
29662966
z += i;
29672967
if( z[0] ){ z += 6; }
29682968
i = 0;
29692969
}else{
2970
- i++;
2970
+ i += strcspn(&z[i+1], "<$") + 1;
29712971
}
29722972
}
29732973
if( rc==TH_ERROR ){
29742974
zResult = (char*)Th_GetResult(g.interp, &n);
29752975
sendError(pOut,zResult, n, 1);
29762976
--- src/th_main.c
+++ src/th_main.c
@@ -2145,11 +2145,11 @@
2145 }
2146 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
2147 if( nArg+2!=argc ){
2148 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
2149 }
2150 zErr = re_compile(&pRe, argv[nArg], noCase);
2151 if( !zErr ){
2152 Th_SetResultInt(interp, re_match(pRe,
2153 (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
2154 rc = TH_OK;
2155 }else{
@@ -2202,11 +2202,11 @@
2202 Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
2203 return TH_ERROR;
2204 }
2205 zRegexp = db_get("th1-uri-regexp", 0);
2206 if( zRegexp && zRegexp[0] ){
2207 const char *zErr = re_compile(&pRe, zRegexp, 0);
2208 if( zErr ){
2209 Th_SetResult(interp, zErr, -1);
2210 return TH_ERROR;
2211 }
2212 }
@@ -2965,11 +2965,11 @@
2965 if( rc!=TH_OK ) break;
2966 z += i;
2967 if( z[0] ){ z += 6; }
2968 i = 0;
2969 }else{
2970 i++;
2971 }
2972 }
2973 if( rc==TH_ERROR ){
2974 zResult = (char*)Th_GetResult(g.interp, &n);
2975 sendError(pOut,zResult, n, 1);
2976
--- src/th_main.c
+++ src/th_main.c
@@ -2145,11 +2145,11 @@
2145 }
2146 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
2147 if( nArg+2!=argc ){
2148 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
2149 }
2150 zErr = fossil_re_compile(&pRe, argv[nArg], noCase);
2151 if( !zErr ){
2152 Th_SetResultInt(interp, re_match(pRe,
2153 (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
2154 rc = TH_OK;
2155 }else{
@@ -2202,11 +2202,11 @@
2202 Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
2203 return TH_ERROR;
2204 }
2205 zRegexp = db_get("th1-uri-regexp", 0);
2206 if( zRegexp && zRegexp[0] ){
2207 const char *zErr = fossil_re_compile(&pRe, zRegexp, 0);
2208 if( zErr ){
2209 Th_SetResult(interp, zErr, -1);
2210 return TH_ERROR;
2211 }
2212 }
@@ -2965,11 +2965,11 @@
2965 if( rc!=TH_OK ) break;
2966 z += i;
2967 if( z[0] ){ z += 6; }
2968 i = 0;
2969 }else{
2970 i += strcspn(&z[i+1], "<$") + 1;
2971 }
2972 }
2973 if( rc==TH_ERROR ){
2974 zResult = (char*)Th_GetResult(g.interp, &n);
2975 sendError(pOut,zResult, n, 1);
2976
+284 -164
--- src/timeline.c
+++ src/timeline.c
@@ -115,11 +115,13 @@
115115
#define TIMELINE_COMPACT 0x0001000 /* Use the "compact" view style */
116116
#define TIMELINE_VERBOSE 0x0002000 /* Use the "detailed" view style */
117117
#define TIMELINE_MODERN 0x0004000 /* Use the "modern" view style */
118118
#define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */
119119
#define TIMELINE_CLASSIC 0x0010000 /* Use the "classic" view style */
120
-#define TIMELINE_VIEWS 0x001f000 /* Mask for all of the view styles */
120
+#define TIMELINE_SIMPLE 0x0020000 /* Use the "simple" view style */
121
+#define TIMELINE_INLINE 0x0033000 /* Mask for views with in-line display */
122
+#define TIMELINE_VIEWS 0x003f000 /* Mask for all of the view styles */
121123
#define TIMELINE_NOSCROLL 0x0100000 /* Don't scroll to the selection */
122124
#define TIMELINE_FILEDIFF 0x0200000 /* Show File differences, not ckin diffs */
123125
#define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */
124126
#define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */
125127
#define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */
@@ -170,10 +172,182 @@
170172
manifest_destroy(pPost);
171173
}
172174
}
173175
174176
177
+/*
178
+** This routine generates the default "extra" text after the description
179
+** in a timeline.
180
+**
181
+** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
182
+**
183
+** This routine is used if no xExtra argument is supplied to
184
+** www_print_timeline().
185
+*/
186
+void timeline_extra(
187
+ Stmt *pQuery, /* Current row of the timeline query */
188
+ int tmFlags, /* Flags to www_print_timeline() */
189
+ const char *zThisUser, /* Suppress links to this user */
190
+ const char *zThisTag /* Suppress links to this tag */
191
+){
192
+ int rid = db_column_int(pQuery, 0);
193
+ const char *zUuid = db_column_text(pQuery, 1);
194
+ const char *zDate = db_column_text(pQuery, 2);
195
+ const char *zType = db_column_text(pQuery, 7);
196
+ const char *zUser = db_column_text(pQuery, 4);
197
+ const char *zTagList = db_column_text(pQuery, 8);
198
+ int tagid = db_column_int(pQuery, 9);
199
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
200
+
201
+ if( (tmFlags & TIMELINE_INLINE)!=0 ){
202
+ cgi_printf("(");
203
+ }
204
+
205
+ if( (tmFlags & TIMELINE_CLASSIC)==0 ){
206
+ if( zType[0]=='c' ){
207
+ const char *zPrefix = 0;
208
+ static int markLeaves = -1;
209
+ if( markLeaves<0 ){
210
+ markLeaves = db_get_int("timeline-mark-leaves",1);
211
+ if( markLeaves<0 ) markLeaves = 1;
212
+ }
213
+ if( strcmp(zUuid, MANIFEST_UUID)==0 ){
214
+ /* This will only ever happen when Fossil is drawing a timeline for
215
+ ** its own self-host repository. If the timeline shows the specific
216
+ ** check-in corresponding to the current executable, then tag that
217
+ ** check-in with "self" */
218
+ zPrefix = "self&nbsp;";
219
+ }else if( markLeaves && db_column_int(pQuery,5) ){
220
+ if( markLeaves==1 ){
221
+ zPrefix = has_closed_tag(rid) ? "closed&nbsp;" : "leaf&nbsp;";
222
+ }else{
223
+ zPrefix = has_closed_tag(rid) ?
224
+ "<span class='timelineLeaf'>Closed-Leaf</span>\n" :
225
+ "<span class='timelineLeaf'>Leaf</span>\n";
226
+ }
227
+ }
228
+ cgi_printf("%scheck-in:&nbsp;%z<span class='timelineHash'>"
229
+ "%S</span></a> ",
230
+ zPrefix, href("%R/info/%!S",zUuid),zUuid);
231
+ }else if( zType[0]=='e' && tagid ){
232
+ cgi_printf("technote:&nbsp;");
233
+ hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
234
+ }else{
235
+ cgi_printf("artifact:&nbsp;%z%S</a> ",
236
+ href("%R/info/%!S",zUuid),zUuid);
237
+ }
238
+ }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
239
+ || zType[0]=='n' || zType[0]=='f'){
240
+ cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
241
+ }
242
+
243
+ if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
244
+ @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
245
+ @ data-id='%d(rid)'>...</span>
246
+ @ <span class='clutter' id='detail-%d(rid)'>
247
+ }
248
+
249
+ if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
250
+ char *zLink;
251
+ if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
252
+ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
253
+ }else{
254
+ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
255
+ }
256
+ cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
257
+ }else{
258
+ cgi_printf("user:&nbsp;%h", zDispUser);
259
+ }
260
+
261
+ /* Generate the "tags: TAGLIST" at the end of the comment, together
262
+ ** with hyperlinks to the tag list.
263
+ */
264
+ if( zTagList && zTagList[0]==0 ) zTagList = 0;
265
+ if( zTagList ){
266
+ if( g.perm.Hyperlink ){
267
+ int i;
268
+ const char *z = zTagList;
269
+ Blob links;
270
+ blob_zero(&links);
271
+ while( z && z[0] ){
272
+ for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
273
+ if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
274
+ blob_appendf(&links,
275
+ "%z%#h</a>%.2s",
276
+ href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
277
+ );
278
+ }else{
279
+ blob_appendf(&links, "%#h", i+2, z);
280
+ }
281
+ if( z[i]==0 ) break;
282
+ z += i+2;
283
+ }
284
+ cgi_printf(" tags:&nbsp;%s", blob_str(&links));
285
+ blob_reset(&links);
286
+ }else{
287
+ cgi_printf(" tags:&nbsp;%h", zTagList);
288
+ }
289
+ }
290
+
291
+ if( tmFlags & TIMELINE_SHOWRID ){
292
+ int srcId = delta_source_rid(rid);
293
+ if( srcId ){
294
+ cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
295
+ href("%R/deltachain/%d",rid), rid, srcId);
296
+ }else{
297
+ cgi_printf(" id:&nbsp;%z%d</a>",
298
+ href("%R/deltachain/%d",rid), rid);
299
+ }
300
+ }
301
+ tag_private_status(rid);
302
+
303
+ if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
304
+ cgi_printf("</span>"); /* End of the declutter span */
305
+ }
306
+
307
+ /* End timelineDetail */
308
+ if( (tmFlags & TIMELINE_INLINE)!=0 ){
309
+ cgi_printf(")");
310
+ }
311
+}
312
+
313
+
314
+/*
315
+** SETTING: timeline-truncate-at-blank boolean default=off
316
+**
317
+** If enabled, check-in comments displayed on the timeline are truncated
318
+** at the first blank line of the comment text. The comment text after
319
+** the first blank line is only seen in the /info or similar pages that
320
+** show details about the check-in.
321
+*/
322
+/*
323
+** SETTING: timeline-tslink-info boolean default=off
324
+**
325
+** The hyperlink on the timestamp associated with each timeline entry,
326
+** on the far left-hand side of the screen, normally targets another
327
+** /timeline page that shows the entry in context. However, if this
328
+** option is turned on, that hyperlink targets the /info page showing
329
+** the details of the entry.
330
+*/
331
+/*
332
+** SETTING: timeline-mark-leaves width=5 default=1
333
+**
334
+** Determine whether or not leaf check-ins are marked as such in the
335
+** details section of the timeline. The value is an integer between 0
336
+** and 2:
337
+**
338
+** 0 Do not show any special marking for leaf check-ins.
339
+**
340
+** 1 Show just "leaf" or "closed"
341
+**
342
+** 2 Show "Leaf" or "Closed-Leaf" with emphasis
343
+**
344
+** The default is currently 1. Prior to 2025-10-19, the default was 2.
345
+** This setting has no effect on the "Classic" view, which always behaves
346
+** as if the setting were 2.
347
+*/
348
+
175349
/*
176350
** Output a timeline in the web format given a query. The query
177351
** should return these columns:
178352
**
179353
** 0. rid
@@ -194,11 +368,11 @@
194368
const char *zThisUser, /* Suppress links to this user */
195369
const char *zThisTag, /* Suppress links to this tag */
196370
Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
197371
int selectedRid, /* Highlight the line with this RID value or zero */
198372
int secondRid, /* Secondary highlight (or zero) */
199
- void (*xExtra)(int) /* Routine to call on each line of display */
373
+ void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
200374
){
201375
int mxWikiLen;
202376
Blob comment;
203377
int prevTagid = 0;
204378
int suppressCnt = 0;
@@ -214,55 +388,41 @@
214388
const char *zStyle; /* Sub-name for classes for the style */
215389
const char *zDateFmt;
216390
int iTableId = timeline_tableid();
217391
int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
218392
** page rather than the /timeline page */
393
+ char *zMainBranch = db_get("main-branch","trunk");
394
+
219395
220396
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
221397
vid = db_lget_int("checkout", 0);
222398
}
399
+ if( xExtra==0 ) xExtra = timeline_extra;
223400
zPrevDate[0] = 0;
224401
mxWikiLen = db_get_int("timeline-max-comment", 0);
225402
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
-*/
234403
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
-*/
244404
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245405
if( (tmFlags & TIMELINE_VIEWS)==0 ){
246406
tmFlags |= timeline_ss_cookie();
247407
}
248408
if( tmFlags & TIMELINE_COLUMNAR ){
249409
zStyle = "Columnar";
250410
}else if( tmFlags & TIMELINE_COMPACT ){
251411
zStyle = "Compact";
412
+ }else if( tmFlags & TIMELINE_SIMPLE ){
413
+ zStyle = "Simple";
252414
}else if( tmFlags & TIMELINE_VERBOSE ){
253415
zStyle = "Verbose";
254416
}else if( tmFlags & TIMELINE_CLASSIC ){
255417
zStyle = "Classic";
256418
}else{
257419
zStyle = "Modern";
258420
}
259421
zDateFmt = P("datefmt");
260422
if( zDateFmt ) dateFormat = atoi(zDateFmt);
261
- if( tmFlags & TIMELINE_GRAPH ){
262
- pGraph = graph_init();
263
- }
423
+ pGraph = graph_init();
264424
if( (tmFlags & TIMELINE_CHPICK)!=0
265425
&& !db_table_exists("repository","cherrypick")
266426
){
267427
tmFlags &= ~TIMELINE_CHPICK;
268428
}
@@ -275,13 +435,11 @@
275435
int isLeaf = db_column_int(pQuery, 5);
276436
const char *zBgClr = db_column_text(pQuery, 6);
277437
const char *zDate = db_column_text(pQuery, 2);
278438
const char *zType = db_column_text(pQuery, 7);
279439
const char *zUser = db_column_text(pQuery, 4);
280
- const char *zTagList = db_column_text(pQuery, 8);
281440
int tagid = db_column_int(pQuery, 9);
282
- const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
283441
char *zBr = 0; /* Branch */
284442
int commentColumn = 3; /* Column containing comment text */
285443
int modPending; /* Pending moderation */
286444
char *zDateLink; /* URL for the link on the timestamp */
287445
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
@@ -434,11 +592,11 @@
434592
zBr = branch_of_rid(rid);
435593
if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
436594
/* If no background color is specified, use a color based on the
437595
** branch name */
438596
if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
439
- }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
597
+ }else if( zBr==0 || strcmp(zBr,zMainBranch)==0 ){
440598
zBgClr = 0;
441599
}else{
442600
zBgClr = hash_color(zBr);
443601
}
444602
}
@@ -446,32 +604,34 @@
446604
if( zType[0]=='c' && pGraph ){
447605
int nParent = 0;
448606
int nCherrypick = 0;
449607
GraphRowId aParent[GR_MAX_RAIL];
450608
static Stmt qparent;
451
- db_static_prepare(&qparent,
452
- "SELECT pid FROM plink"
453
- " WHERE cid=:rid AND pid NOT IN phantom"
454
- " ORDER BY isprim DESC /*sort*/"
455
- );
456
- db_bind_int(&qparent, ":rid", rid);
457
- while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
458
- aParent[nParent++] = db_column_int(&qparent, 0);
459
- }
460
- db_reset(&qparent);
461
- if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){
462
- static Stmt qcherrypick;
463
- db_static_prepare(&qcherrypick,
464
- "SELECT parentid FROM cherrypick"
465
- " WHERE childid=:rid AND parentid NOT IN phantom"
466
- );
467
- db_bind_int(&qcherrypick, ":rid", rid);
468
- while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
469
- aParent[nParent++] = db_column_int(&qcherrypick, 0);
470
- nCherrypick++;
471
- }
472
- db_reset(&qcherrypick);
609
+ if( tmFlags & TIMELINE_GRAPH ){
610
+ db_static_prepare(&qparent,
611
+ "SELECT pid FROM plink"
612
+ " WHERE cid=:rid AND pid NOT IN phantom"
613
+ " ORDER BY isprim DESC /*sort*/"
614
+ );
615
+ db_bind_int(&qparent, ":rid", rid);
616
+ while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
617
+ aParent[nParent++] = db_column_int(&qparent, 0);
618
+ }
619
+ db_reset(&qparent);
620
+ if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){
621
+ static Stmt qcherrypick;
622
+ db_static_prepare(&qcherrypick,
623
+ "SELECT parentid FROM cherrypick"
624
+ " WHERE childid=:rid AND parentid NOT IN phantom"
625
+ );
626
+ db_bind_int(&qcherrypick, ":rid", rid);
627
+ while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
628
+ aParent[nParent++] = db_column_int(&qcherrypick, 0);
629
+ nCherrypick++;
630
+ }
631
+ db_reset(&qcherrypick);
632
+ }
473633
}
474634
gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
475635
zBr, zBgClr, zUuid,
476636
isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
477637
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
@@ -598,23 +758,15 @@
598758
drawDetailEllipsis = 0;
599759
}else{
600760
cgi_printf("%W",blob_str(&comment));
601761
}
602762
}
603
-
604
- if( zType[0]=='c' && strcmp(zUuid, MANIFEST_UUID)==0 ){
605
- /* This will only ever happen when Fossil is drawing a timeline for
606
- ** its own self-host repository. If the timeline shows the specific
607
- ** check-in corresponding to the current executable, then tag that
608
- ** check-in with "This is me!". */
609
- @ <b>&larr; This is me!</b>
610
- }
611763
612764
@ </span>
613765
blob_reset(&comment);
614766
615
- /* Generate extra information and hyperlinks to follow the comment.
767
+ /* Generate extra information and hyperlinks that follow the comment.
616768
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
617769
*/
618770
if( drawDetailEllipsis ){
619771
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
620772
@ data-id='%d(rid)'>...</span>
@@ -628,101 +780,17 @@
628780
}
629781
if( tmFlags & TIMELINE_COMPACT ){
630782
cgi_printf("<span class='clutter' id='detail-%d'>",rid);
631783
}
632784
cgi_printf("<span class='timeline%sDetail'>", zStyle);
633
- if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
634
- cgi_printf("(");
635
- }
636
-
637
- if( (tmFlags & TIMELINE_CLASSIC)==0 ){
638
- if( zType[0]=='c' ){
639
- if( isLeaf ){
640
- if( has_closed_tag(rid) ){
641
- @ <span class='timelineLeaf'>Closed-Leaf</span>
642
- }else{
643
- @ <span class='timelineLeaf'>Leaf</span>
644
- }
645
- }
646
- cgi_printf("check-in:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
647
- }else if( zType[0]=='e' && tagid ){
648
- cgi_printf("technote:&nbsp;");
649
- hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
650
- }else{
651
- cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
652
- }
653
- }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
654
- || zType[0]=='n' || zType[0]=='f'){
655
- cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
656
- }
657
-
658
- if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
659
- char *zLink;
660
- if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
661
- zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
662
- }else{
663
- zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
664
- }
665
- cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
666
- }else{
667
- cgi_printf("user:&nbsp;%h", zDispUser);
668
- }
669
-
670
- /* Generate the "tags: TAGLIST" at the end of the comment, together
671
- ** with hyperlinks to the tag list.
672
- */
673
- if( zTagList && zTagList[0]==0 ) zTagList = 0;
674
- if( zTagList ){
675
- if( g.perm.Hyperlink ){
676
- int i;
677
- const char *z = zTagList;
678
- Blob links;
679
- blob_zero(&links);
680
- while( z && z[0] ){
681
- for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
682
- if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
683
- blob_appendf(&links,
684
- "%z%#h</a>%.2s",
685
- href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
686
- );
687
- }else{
688
- blob_appendf(&links, "%#h", i+2, z);
689
- }
690
- if( z[i]==0 ) break;
691
- z += i+2;
692
- }
693
- cgi_printf(" tags:&nbsp;%s", blob_str(&links));
694
- blob_reset(&links);
695
- }else{
696
- cgi_printf(" tags:&nbsp;%h", zTagList);
697
- }
698
- }
699
-
700
- if( tmFlags & TIMELINE_SHOWRID ){
701
- int srcId = delta_source_rid(rid);
702
- if( srcId ){
703
- cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
704
- href("%R/deltachain/%d",rid), rid, srcId);
705
- }else{
706
- cgi_printf(" id:&nbsp;%z%d</a>",
707
- href("%R/deltachain/%d",rid), rid);
708
- }
709
- }
710
- tag_private_status(rid);
711
- if( xExtra ){
712
- xExtra(rid);
713
- }
714
- /* End timelineDetail */
715
- if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
716
- cgi_printf(")");
717
- }
785
+ xExtra(pQuery, tmFlags, zThisUser, zThisTag);
718786
if( tmFlags & TIMELINE_COMPACT ){
719787
@ </span></span>
720788
}else{
721789
@ </span>
722790
}
723
-
791
+
724792
/* Generate the file-change list if requested */
725793
if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
726794
&& zType[0]=='c' && g.perm.Hyperlink
727795
){
728796
int inUl = 0;
@@ -908,11 +976,13 @@
908976
int omitDescenders; /* True to omit descenders */
909977
int scrollToSelect; /* True to scroll to the selection */
910978
int dwellTimeout; /* Milliseconds to wait for tooltips to show */
911979
int closeTimeout; /* Milliseconds to wait for tooltips to close */
912980
u8 *aiMap; /* The rail map */
981
+ u8 bNoGraph; /* True to show a minimal graph */
913982
983
+ bNoGraph = (tmFlags & TIMELINE_GRAPH)==0;
914984
iRailPitch = atoi(PD("railpitch","0"));
915985
showArrowheads = skin_detail_boolean("timeline-arrowheads");
916986
circleNodes = skin_detail_boolean("timeline-circle-nodes");
917987
colorGraph = skin_detail_boolean("timeline-color-graph-lines");
918988
iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
@@ -930,11 +1000,11 @@
9301000
@ "nomo": %d(PB("nomo")),
9311001
@ "iTopRow": %d(iTopRow),
9321002
@ "omitDescenders": %d(omitDescenders),
9331003
@ "fileDiff": %d(fileDiff),
9341004
@ "scrollToSelect": %d(scrollToSelect),
935
- @ "nrail": %d(pGraph->mxRail+1),
1005
+ @ "nrail": %d(bNoGraph?1:pGraph->mxRail+1),
9361006
@ "baseUrl": "%R",
9371007
@ "dwellTimeout": %d(dwellTimeout),
9381008
@ "closeTimeout": %d(closeTimeout),
9391009
@ "hashDigits": %d(hash_digits(1)),
9401010
@ "bottomRowId": "btm-%d(iTableId)",
@@ -992,12 +1062,16 @@
9921062
aiMap = pGraph->aiRailMap;
9931063
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
9941064
int k = 0;
9951065
cgi_printf("{\"id\":%d,", pRow->idx);
9961066
cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
997
- cgi_printf("\"r\":%d,", pRow->iRail>=0 ? aiMap[pRow->iRail] : -1);
998
- if( pRow->bDescender ){
1067
+ if( bNoGraph ){
1068
+ cgi_printf("\"r\":0,"); /* Chng to ":-1" to omit node circles */
1069
+ }else{
1070
+ cgi_printf("\"r\":%d,", pRow->iRail>=0 ? aiMap[pRow->iRail] : -1);
1071
+ }
1072
+ if( pRow->bDescender && !bNoGraph ){
9991073
cgi_printf("\"d\":%d,", pRow->bDescender);
10001074
}
10011075
if( pRow->mergeOut>=0 ){
10021076
cgi_printf("\"mo\":%d,", aiMap[pRow->mergeOut]);
10031077
if( pRow->mergeUpto==0 ) pRow->mergeUpto = pRow->idx;
@@ -1004,11 +1078,13 @@
10041078
cgi_printf("\"mu\":%d,", pRow->mergeUpto);
10051079
if( pRow->cherrypickUpto>0 && pRow->cherrypickUpto<=pRow->mergeUpto ){
10061080
cgi_printf("\"cu\":%d,", pRow->cherrypickUpto);
10071081
}
10081082
}
1009
- if( pRow->isStepParent ){
1083
+ if( bNoGraph ){
1084
+ cgi_printf("\"u\":-1,");
1085
+ }else if( pRow->isStepParent ){
10101086
cgi_printf("\"sb\":%d,", pRow->aiRiser[pRow->iRail]);
10111087
}else{
10121088
cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
10131089
}
10141090
k = 0;
@@ -1206,10 +1282,26 @@
12061282
if( i>2 ){
12071283
style_submenu_multichoice("y", i/2, az, isDisabled);
12081284
}
12091285
}
12101286
1287
+/*
1288
+** SETTING: timeline-default-style width=5 default=m
1289
+**
1290
+** This setting determines the default "view style" for timelines.
1291
+** The setting should be a single character, one of the following:
1292
+**
1293
+** c Compact
1294
+** j Columnar
1295
+** m Modern
1296
+** s Simple
1297
+** v Verbose
1298
+** x Classic
1299
+**
1300
+** The default value is m (Modern).
1301
+*/
1302
+
12111303
/*
12121304
** Return the default value for the "ss" cookie or query parameter.
12131305
** The "ss" cookie determines the graph style. See the
12141306
** timeline_view_styles[] global constant for a list of choices.
12151307
*/
@@ -1230,10 +1322,11 @@
12301322
switch( v[0] ){
12311323
case 'c': tmFlags = TIMELINE_COMPACT; break;
12321324
case 'v': tmFlags = TIMELINE_VERBOSE; break;
12331325
case 'j': tmFlags = TIMELINE_COLUMNAR; break;
12341326
case 'x': tmFlags = TIMELINE_CLASSIC; break;
1327
+ case 's': tmFlags = TIMELINE_SIMPLE; break;
12351328
default: tmFlags = TIMELINE_MODERN; break;
12361329
}
12371330
return tmFlags;
12381331
}
12391332
@@ -1242,15 +1335,16 @@
12421335
*/
12431336
const char *const timeline_view_styles[] = {
12441337
"m", "Modern View",
12451338
"j", "Columnar View",
12461339
"c", "Compact View",
1340
+ "s", "Simple View",
12471341
"v", "Verbose View",
12481342
"x", "Classic View",
12491343
};
12501344
#if INTERFACE
1251
-# define N_TIMELINE_VIEW_STYLE 5
1345
+# define N_TIMELINE_VIEW_STYLE 6
12521346
#endif
12531347
12541348
/*
12551349
** Add the select/option box to the timeline submenu that is used to
12561350
** set the ss= parameter that determines the viewing mode.
@@ -1363,11 +1457,11 @@
13631457
** the input is a valid date space and false if not.
13641458
*/
13651459
static int timeline_is_datespan(const char *zDay){
13661460
size_t n = strlen(zDay);
13671461
int i, d, m;
1368
-
1462
+
13691463
if( n<17 || n>18 ) return 0;
13701464
if( n==18 ){
13711465
if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
13721466
n--;
13731467
}
@@ -1389,17 +1483,17 @@
13891483
13901484
/*
13911485
** Find the first check-in encountered with a particular tag
13921486
** when moving either forwards are backwards in time from a
13931487
** particular starting point (iFrom). Return the rid of that
1394
-** first check-in. If there are no check-ins in the decendent
1488
+** first check-in. If there are no check-ins in the descendent
13951489
** or ancestor set of check-in iFrom that match the tag, then
13961490
** return 0.
13971491
*/
13981492
static int timeline_endpoint(
13991493
int iFrom, /* Starting point */
1400
- const char *zEnd, /* Tag we are searching for */
1494
+ const char *zEnd, /* Tag we are searching for */
14011495
int bForward /* 1: forwards in time (descendants) 0: backwards */
14021496
){
14031497
int tagId;
14041498
int endId = 0;
14051499
Stmt q;
@@ -1590,11 +1684,11 @@
15901684
** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2'
15911685
** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
15921686
** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN
15931687
** p=CX ... from CX back to time of CHECKIN
15941688
** from=CX ... path from CX back to CHECKIN
1595
-** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
1689
+** ft=CHECKIN "Forward To": Show descendents forward to CHECKIN
15961690
** d=CX ... from CX up to the time of CHECKIN
15971691
** from=CX ... path from CX up to CHECKIN
15981692
** t=TAG Show only check-ins with the given TAG
15991693
** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
16001694
** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
@@ -1607,11 +1701,11 @@
16071701
** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
16081702
** "like", or "regexp".
16091703
** u=USER Only show items associated with USER
16101704
** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
16111705
** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1612
-* x: "Classic".
1706
+** x: "Classic".
16131707
** advm Use the "Advanced" or "Busy" menu design.
16141708
** ng No Graph.
16151709
** ncp Omit cherrypick merges
16161710
** nd Do not highlight the focus check-in
16171711
** nsm Omit the submenu
@@ -1830,10 +1924,13 @@
18301924
|| (bisectLocal && !g.perm.Setup)
18311925
){
18321926
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
18331927
return;
18341928
}
1929
+ if( zBefore || zCirca ){
1930
+ if( robot_restrict("timelineX") ) return;
1931
+ }
18351932
if( !bisectLocal ){
18361933
etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
18371934
}
18381935
cookie_read_parameter("y","y");
18391936
zType = P("y");
@@ -1969,10 +2066,11 @@
19692066
}
19702067
if( showSql ) db_append_dml_to_blob(&allSql);
19712068
if( zUses!=0 ){
19722069
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
19732070
if( ufid ){
2071
+ if( robot_restrict("timelineX") ) return;
19742072
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
19752073
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
19762074
compute_uses_file("usesfile", ufid, 0);
19772075
zType = "ci";
19782076
disableY = 1;
@@ -2042,11 +2140,11 @@
20422140
zBisect = 0;
20432141
}
20442142
20452143
style_header("Timeline");
20462144
if( advancedMenu ){
2047
- style_submenu_element("Help", "%R/help?cmd=/timeline");
2145
+ style_submenu_element("Help", "%R/help/www/timeline");
20482146
}
20492147
login_anonymous_available();
20502148
timeline_temp_table();
20512149
blob_zero(&sql);
20522150
blob_zero(&desc);
@@ -2283,11 +2381,11 @@
22832381
if( zError==0 ){
22842382
zError = "Cannot use the ft= query parameter when both p= and d= "
22852383
"are used and have distinct values.";
22862384
}
22872385
zFwdTo = 0;
2288
- }
2386
+ }
22892387
if( zFwdTo ){
22902388
double rStartDate = mtime_of_rid(d_rid, 0.0);
22912389
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
22922390
if( ridFwdTo==0 ){
22932391
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
@@ -2342,11 +2440,11 @@
23422440
if( zError==0 ){
23432441
zError = "Cannot use the bt= query parameter when both p= and d= "
23442442
"are used and have distinct values.";
23452443
}
23462444
zBackTo = 0;
2347
- }
2445
+ }
23482446
if( zBackTo ){
23492447
double rDateLimit = mtime_of_rid(p_rid, 0.0);
23502448
ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
23512449
if( ridBackTo==0 ){
23522450
ridBackTo = name_to_typed_rid(zBackTo,"ci");
@@ -2368,11 +2466,11 @@
23682466
}else{
23692467
removeFileGlobFromOk(zChng);
23702468
np = db_int(0, "SELECT count(*)-1 FROM ok");
23712469
if( np>0 || nd==0 ){
23722470
if( nd>0 ) blob_appendf(&desc, " and ");
2373
- blob_appendf(&desc, "%d ancestor%s",
2471
+ blob_appendf(&desc, "%d ancestor%s",
23742472
np>=0 ? np : 0, (1==np)?"":"s");
23752473
db_multi_exec("%s", blob_sql_text(&sql));
23762474
}
23772475
if( useDividers && !selectedRid ) selectedRid = p_rid;
23782476
}
@@ -2402,11 +2500,11 @@
24022500
href("%R/info?name=%h",zBaseName), zBaseName,
24032501
href("%R/info?name=%h",zBackTo), zBackTo);
24042502
}else{
24052503
blob_appendf(&desc, " back to %z%h</a>%s",
24062504
href("%R/info?name=%h",zBackTo), zBackTo,
2407
- bBackAdded ? " (not a direct anscestor)" : "");
2505
+ bBackAdded ? " (not a direct ancestor)" : "");
24082506
if( ridFwdTo && zFwdTo ){
24092507
blob_appendf(&desc, " and up to %z%h</a>%s",
24102508
href("%R/info?name=%h",zFwdTo), zFwdTo,
24112509
bFwdAdded ? " (not a direct descendant)" : "");
24122510
}
@@ -2651,11 +2749,11 @@
26512749
blob_append_sql(&cond,
26522750
" AND event.mtime>=julianday(%Q,%Q)"
26532751
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
26542752
zStart, zTZMod, zEnd, zTZMod);
26552753
nEntry = -1;
2656
-
2754
+
26572755
if( fossil_ui_localtime() && bZulu ){
26582756
zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
26592757
}else{
26602758
zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
26612759
}
@@ -3107,10 +3205,11 @@
31073205
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
31083206
}
31093207
blob_zero(&sql);
31103208
if( PB("oldestfirst") ){
31113209
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
3210
+ tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK);
31123211
}else{
31133212
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
31143213
}
31153214
if( fossil_islower(desc.aData[0]) ){
31163215
desc.aData[0] = fossil_toupper(desc.aData[0]);
@@ -3139,10 +3238,20 @@
31393238
31403239
/* Report any errors. */
31413240
if( zError ){
31423241
@ <p class="generalError">%h(zError)</p>
31433242
}
3243
+
3244
+ /* Swap zNewer and zOlder buttons if we display oldestfirst */
3245
+ if( PB("oldestfirst") ){
3246
+ char *zSwap = zNewerButton;
3247
+ char *zSwapLabel = zNewerButtonLabel;
3248
+ zNewerButton = zOlderButton;
3249
+ zNewerButtonLabel = zOlderButtonLabel;
3250
+ zOlderButton = zSwap;
3251
+ zOlderButtonLabel = zSwapLabel;
3252
+ }
31443253
31453254
if( zNewerButton ){
31463255
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
31473256
@ &nbsp;&uarr;</a>
31483257
}
@@ -3620,19 +3729,21 @@
36203729
** N is negative, output the first -N lines. If N is
36213730
** zero, no limit. Default is -20 meaning 20 lines.
36223731
** --offset P Skip P changes
36233732
** -p|--path PATH Output items affecting PATH only.
36243733
** PATH can be a file or a sub directory.
3734
+** -r|--reverse Show items in chronological order.
36253735
** -R REPO_FILE Specifies the repository db to use. Default is
36263736
** the current check-out's repository.
36273737
** --sql Show the SQL used to generate the timeline
36283738
** -t|--type TYPE Output items from the given types only, such as:
36293739
** ci = file commits only
36303740
** e = technical notes only
36313741
** f = forum posts only
36323742
** t = tickets only
36333743
** w = wiki commits only
3744
+** -u|--for-user USER Only show items associated with USER
36343745
** -v|--verbose Output the list of files changed by each commit
36353746
** and the type of each change (edited, deleted,
36363747
** etc.) after the check-in comment.
36373748
** -W|--width N Width of lines (default is to auto-detect). N must be
36383749
** either greater than 20 or it must be zero 0 to
@@ -3644,17 +3755,19 @@
36443755
int n, k, width;
36453756
const char *zLimit;
36463757
const char *zWidth;
36473758
const char *zOffset;
36483759
const char *zType;
3760
+ const char *zUser;
36493761
char *zOrigin;
36503762
char *zDate;
36513763
Blob sql;
36523764
int objid = 0;
36533765
Blob uuid;
36543766
int mode = TIMELINE_MODE_NONE;
3655
- int verboseFlag = 0 ;
3767
+ int verboseFlag = 0;
3768
+ int reverseFlag = 0;
36563769
int iOffset;
36573770
const char *zFilePattern = 0;
36583771
const char *zFormat = 0;
36593772
const char *zBr = 0;
36603773
Blob treeName;
@@ -3666,10 +3779,11 @@
36663779
}
36673780
db_find_and_open_repository(0, 0);
36683781
zLimit = find_option("limit","n",1);
36693782
zWidth = find_option("width","W",1);
36703783
zType = find_option("type","t",1);
3784
+ zUser = find_option("for-user","u",1);
36713785
zFilePattern = find_option("path","p",1);
36723786
zFormat = find_option("format","F",1);
36733787
zBr = find_option("branch","b",1);
36743788
if( find_option("current-branch","c",0)!=0 ){
36753789
if( !g.localOpen ){
@@ -3707,10 +3821,11 @@
37073821
}else{
37083822
width = -1;
37093823
}
37103824
zOffset = find_option("offset",0,1);
37113825
iOffset = zOffset ? atoi(zOffset) : 0;
3826
+ reverseFlag = find_option("reverse","r",0)!=0;
37123827
37133828
/* We should be done with options.. */
37143829
verify_all_options();
37153830
37163831
if( g.argc>=4 ){
@@ -3727,11 +3842,11 @@
37273842
mode = TIMELINE_MODE_PARENTS;
37283843
}else if( strncmp(g.argv[2],"parents",k)==0 ){
37293844
mode = TIMELINE_MODE_PARENTS;
37303845
}else if(!zType && !zLimit){
37313846
usage("?WHEN? ?CHECKIN|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
3732
- "?-W|--width WIDTH? ?-p|--path PATH?");
3847
+ "?-W|--width WIDTH? ?-p|--path PATH? ?-r|--reverse?");
37333848
}
37343849
if( '-' != *g.argv[3] ){
37353850
zOrigin = g.argv[3];
37363851
}else{
37373852
zOrigin = "now";
@@ -3797,10 +3912,13 @@
37973912
mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
37983913
);
37993914
if( zType && (zType[0]!='a') ){
38003915
blob_append_sql(&sql, "\n AND event.type=%Q ", zType);
38013916
}
3917
+ if( zUser && (zUser[0]!='\0') ){
3918
+ blob_append_sql(&sql, "\n AND user0=%Q ", zUser);
3919
+ }
38023920
38033921
/* When zFilePattern is specified, compute complete ancestry;
38043922
* limit later at print_timeline() */
38053923
if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
38063924
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
@@ -3850,23 +3968,25 @@
38503968
" WHERE tx.value='%q'\n"
38513969
")\n" /* No merge closures */
38523970
" AND (tagxref.value IS NULL OR tagxref.value='%q')",
38533971
zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
38543972
}
3855
-
3973
+
38563974
if( mode==TIMELINE_MODE_AFTER ){
38573975
int lim = n;
38583976
if( n == 0 ){
38593977
lim = -1; /* 0 means no limit */
38603978
}else if( n < 0 ){
38613979
lim = -n;
38623980
}
38633981
/* Complete the above outer select. */
3864
- blob_append_sql(&sql,
3865
- "\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime DESC;", lim);
3982
+ blob_append_sql(&sql,
3983
+ "\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime %s",
3984
+ lim, reverseFlag ? "" : "DESC");
38663985
}else{
3867
- blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
3986
+ blob_append_sql(&sql,
3987
+ "\nORDER BY event.mtime %s", reverseFlag ? "" : "DESC");
38683988
}
38693989
if( iOffset>0 ){
38703990
/* Don't handle LIMIT here, otherwise print_timeline()
38713991
* will not determine the end-marker correctly! */
38723992
blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
38733993
--- src/timeline.c
+++ src/timeline.c
@@ -115,11 +115,13 @@
115 #define TIMELINE_COMPACT 0x0001000 /* Use the "compact" view style */
116 #define TIMELINE_VERBOSE 0x0002000 /* Use the "detailed" view style */
117 #define TIMELINE_MODERN 0x0004000 /* Use the "modern" view style */
118 #define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */
119 #define TIMELINE_CLASSIC 0x0010000 /* Use the "classic" view style */
120 #define TIMELINE_VIEWS 0x001f000 /* Mask for all of the view styles */
 
 
121 #define TIMELINE_NOSCROLL 0x0100000 /* Don't scroll to the selection */
122 #define TIMELINE_FILEDIFF 0x0200000 /* Show File differences, not ckin diffs */
123 #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */
124 #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */
125 #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */
@@ -170,10 +172,182 @@
170 manifest_destroy(pPost);
171 }
172 }
173
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175 /*
176 ** Output a timeline in the web format given a query. The query
177 ** should return these columns:
178 **
179 ** 0. rid
@@ -194,11 +368,11 @@
194 const char *zThisUser, /* Suppress links to this user */
195 const char *zThisTag, /* Suppress links to this tag */
196 Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
197 int selectedRid, /* Highlight the line with this RID value or zero */
198 int secondRid, /* Secondary highlight (or zero) */
199 void (*xExtra)(int) /* Routine to call on each line of display */
200 ){
201 int mxWikiLen;
202 Blob comment;
203 int prevTagid = 0;
204 int suppressCnt = 0;
@@ -214,55 +388,41 @@
214 const char *zStyle; /* Sub-name for classes for the style */
215 const char *zDateFmt;
216 int iTableId = timeline_tableid();
217 int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
218 ** page rather than the /timeline page */
 
 
219
220 if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
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 ){
249 zStyle = "Columnar";
250 }else if( tmFlags & TIMELINE_COMPACT ){
251 zStyle = "Compact";
 
 
252 }else if( tmFlags & TIMELINE_VERBOSE ){
253 zStyle = "Verbose";
254 }else if( tmFlags & TIMELINE_CLASSIC ){
255 zStyle = "Classic";
256 }else{
257 zStyle = "Modern";
258 }
259 zDateFmt = P("datefmt");
260 if( zDateFmt ) dateFormat = atoi(zDateFmt);
261 if( tmFlags & TIMELINE_GRAPH ){
262 pGraph = graph_init();
263 }
264 if( (tmFlags & TIMELINE_CHPICK)!=0
265 && !db_table_exists("repository","cherrypick")
266 ){
267 tmFlags &= ~TIMELINE_CHPICK;
268 }
@@ -275,13 +435,11 @@
275 int isLeaf = db_column_int(pQuery, 5);
276 const char *zBgClr = db_column_text(pQuery, 6);
277 const char *zDate = db_column_text(pQuery, 2);
278 const char *zType = db_column_text(pQuery, 7);
279 const char *zUser = db_column_text(pQuery, 4);
280 const char *zTagList = db_column_text(pQuery, 8);
281 int tagid = db_column_int(pQuery, 9);
282 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
283 char *zBr = 0; /* Branch */
284 int commentColumn = 3; /* Column containing comment text */
285 int modPending; /* Pending moderation */
286 char *zDateLink; /* URL for the link on the timestamp */
287 int drawDetailEllipsis; /* True to show ellipsis in place of detail */
@@ -434,11 +592,11 @@
434 zBr = branch_of_rid(rid);
435 if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
436 /* If no background color is specified, use a color based on the
437 ** branch name */
438 if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
439 }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
440 zBgClr = 0;
441 }else{
442 zBgClr = hash_color(zBr);
443 }
444 }
@@ -446,32 +604,34 @@
446 if( zType[0]=='c' && pGraph ){
447 int nParent = 0;
448 int nCherrypick = 0;
449 GraphRowId aParent[GR_MAX_RAIL];
450 static Stmt qparent;
451 db_static_prepare(&qparent,
452 "SELECT pid FROM plink"
453 " WHERE cid=:rid AND pid NOT IN phantom"
454 " ORDER BY isprim DESC /*sort*/"
455 );
456 db_bind_int(&qparent, ":rid", rid);
457 while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
458 aParent[nParent++] = db_column_int(&qparent, 0);
459 }
460 db_reset(&qparent);
461 if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){
462 static Stmt qcherrypick;
463 db_static_prepare(&qcherrypick,
464 "SELECT parentid FROM cherrypick"
465 " WHERE childid=:rid AND parentid NOT IN phantom"
466 );
467 db_bind_int(&qcherrypick, ":rid", rid);
468 while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
469 aParent[nParent++] = db_column_int(&qcherrypick, 0);
470 nCherrypick++;
471 }
472 db_reset(&qcherrypick);
 
 
473 }
474 gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
475 zBr, zBgClr, zUuid,
476 isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
477 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
@@ -598,23 +758,15 @@
598 drawDetailEllipsis = 0;
599 }else{
600 cgi_printf("%W",blob_str(&comment));
601 }
602 }
603
604 if( zType[0]=='c' && strcmp(zUuid, MANIFEST_UUID)==0 ){
605 /* This will only ever happen when Fossil is drawing a timeline for
606 ** its own self-host repository. If the timeline shows the specific
607 ** check-in corresponding to the current executable, then tag that
608 ** check-in with "This is me!". */
609 @ <b>&larr; This is me!</b>
610 }
611
612 @ </span>
613 blob_reset(&comment);
614
615 /* Generate extra information and hyperlinks to follow the comment.
616 ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
617 */
618 if( drawDetailEllipsis ){
619 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
620 @ data-id='%d(rid)'>...</span>
@@ -628,101 +780,17 @@
628 }
629 if( tmFlags & TIMELINE_COMPACT ){
630 cgi_printf("<span class='clutter' id='detail-%d'>",rid);
631 }
632 cgi_printf("<span class='timeline%sDetail'>", zStyle);
633 if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
634 cgi_printf("(");
635 }
636
637 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
638 if( zType[0]=='c' ){
639 if( isLeaf ){
640 if( has_closed_tag(rid) ){
641 @ <span class='timelineLeaf'>Closed-Leaf</span>
642 }else{
643 @ <span class='timelineLeaf'>Leaf</span>
644 }
645 }
646 cgi_printf("check-in:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
647 }else if( zType[0]=='e' && tagid ){
648 cgi_printf("technote:&nbsp;");
649 hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
650 }else{
651 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
652 }
653 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
654 || zType[0]=='n' || zType[0]=='f'){
655 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
656 }
657
658 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
659 char *zLink;
660 if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
661 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
662 }else{
663 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
664 }
665 cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
666 }else{
667 cgi_printf("user:&nbsp;%h", zDispUser);
668 }
669
670 /* Generate the "tags: TAGLIST" at the end of the comment, together
671 ** with hyperlinks to the tag list.
672 */
673 if( zTagList && zTagList[0]==0 ) zTagList = 0;
674 if( zTagList ){
675 if( g.perm.Hyperlink ){
676 int i;
677 const char *z = zTagList;
678 Blob links;
679 blob_zero(&links);
680 while( z && z[0] ){
681 for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
682 if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
683 blob_appendf(&links,
684 "%z%#h</a>%.2s",
685 href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
686 );
687 }else{
688 blob_appendf(&links, "%#h", i+2, z);
689 }
690 if( z[i]==0 ) break;
691 z += i+2;
692 }
693 cgi_printf(" tags:&nbsp;%s", blob_str(&links));
694 blob_reset(&links);
695 }else{
696 cgi_printf(" tags:&nbsp;%h", zTagList);
697 }
698 }
699
700 if( tmFlags & TIMELINE_SHOWRID ){
701 int srcId = delta_source_rid(rid);
702 if( srcId ){
703 cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
704 href("%R/deltachain/%d",rid), rid, srcId);
705 }else{
706 cgi_printf(" id:&nbsp;%z%d</a>",
707 href("%R/deltachain/%d",rid), rid);
708 }
709 }
710 tag_private_status(rid);
711 if( xExtra ){
712 xExtra(rid);
713 }
714 /* End timelineDetail */
715 if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
716 cgi_printf(")");
717 }
718 if( tmFlags & TIMELINE_COMPACT ){
719 @ </span></span>
720 }else{
721 @ </span>
722 }
723
724 /* Generate the file-change list if requested */
725 if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
726 && zType[0]=='c' && g.perm.Hyperlink
727 ){
728 int inUl = 0;
@@ -908,11 +976,13 @@
908 int omitDescenders; /* True to omit descenders */
909 int scrollToSelect; /* True to scroll to the selection */
910 int dwellTimeout; /* Milliseconds to wait for tooltips to show */
911 int closeTimeout; /* Milliseconds to wait for tooltips to close */
912 u8 *aiMap; /* The rail map */
 
913
 
914 iRailPitch = atoi(PD("railpitch","0"));
915 showArrowheads = skin_detail_boolean("timeline-arrowheads");
916 circleNodes = skin_detail_boolean("timeline-circle-nodes");
917 colorGraph = skin_detail_boolean("timeline-color-graph-lines");
918 iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
@@ -930,11 +1000,11 @@
930 @ "nomo": %d(PB("nomo")),
931 @ "iTopRow": %d(iTopRow),
932 @ "omitDescenders": %d(omitDescenders),
933 @ "fileDiff": %d(fileDiff),
934 @ "scrollToSelect": %d(scrollToSelect),
935 @ "nrail": %d(pGraph->mxRail+1),
936 @ "baseUrl": "%R",
937 @ "dwellTimeout": %d(dwellTimeout),
938 @ "closeTimeout": %d(closeTimeout),
939 @ "hashDigits": %d(hash_digits(1)),
940 @ "bottomRowId": "btm-%d(iTableId)",
@@ -992,12 +1062,16 @@
992 aiMap = pGraph->aiRailMap;
993 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
994 int k = 0;
995 cgi_printf("{\"id\":%d,", pRow->idx);
996 cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
997 cgi_printf("\"r\":%d,", pRow->iRail>=0 ? aiMap[pRow->iRail] : -1);
998 if( pRow->bDescender ){
 
 
 
 
999 cgi_printf("\"d\":%d,", pRow->bDescender);
1000 }
1001 if( pRow->mergeOut>=0 ){
1002 cgi_printf("\"mo\":%d,", aiMap[pRow->mergeOut]);
1003 if( pRow->mergeUpto==0 ) pRow->mergeUpto = pRow->idx;
@@ -1004,11 +1078,13 @@
1004 cgi_printf("\"mu\":%d,", pRow->mergeUpto);
1005 if( pRow->cherrypickUpto>0 && pRow->cherrypickUpto<=pRow->mergeUpto ){
1006 cgi_printf("\"cu\":%d,", pRow->cherrypickUpto);
1007 }
1008 }
1009 if( pRow->isStepParent ){
 
 
1010 cgi_printf("\"sb\":%d,", pRow->aiRiser[pRow->iRail]);
1011 }else{
1012 cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
1013 }
1014 k = 0;
@@ -1206,10 +1282,26 @@
1206 if( i>2 ){
1207 style_submenu_multichoice("y", i/2, az, isDisabled);
1208 }
1209 }
1210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1211 /*
1212 ** Return the default value for the "ss" cookie or query parameter.
1213 ** The "ss" cookie determines the graph style. See the
1214 ** timeline_view_styles[] global constant for a list of choices.
1215 */
@@ -1230,10 +1322,11 @@
1230 switch( v[0] ){
1231 case 'c': tmFlags = TIMELINE_COMPACT; break;
1232 case 'v': tmFlags = TIMELINE_VERBOSE; break;
1233 case 'j': tmFlags = TIMELINE_COLUMNAR; break;
1234 case 'x': tmFlags = TIMELINE_CLASSIC; break;
 
1235 default: tmFlags = TIMELINE_MODERN; break;
1236 }
1237 return tmFlags;
1238 }
1239
@@ -1242,15 +1335,16 @@
1242 */
1243 const char *const timeline_view_styles[] = {
1244 "m", "Modern View",
1245 "j", "Columnar View",
1246 "c", "Compact View",
 
1247 "v", "Verbose View",
1248 "x", "Classic View",
1249 };
1250 #if INTERFACE
1251 # define N_TIMELINE_VIEW_STYLE 5
1252 #endif
1253
1254 /*
1255 ** Add the select/option box to the timeline submenu that is used to
1256 ** set the ss= parameter that determines the viewing mode.
@@ -1363,11 +1457,11 @@
1363 ** the input is a valid date space and false if not.
1364 */
1365 static int timeline_is_datespan(const char *zDay){
1366 size_t n = strlen(zDay);
1367 int i, d, m;
1368
1369 if( n<17 || n>18 ) return 0;
1370 if( n==18 ){
1371 if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
1372 n--;
1373 }
@@ -1389,17 +1483,17 @@
1389
1390 /*
1391 ** Find the first check-in encountered with a particular tag
1392 ** when moving either forwards are backwards in time from a
1393 ** particular starting point (iFrom). Return the rid of that
1394 ** first check-in. If there are no check-ins in the decendent
1395 ** or ancestor set of check-in iFrom that match the tag, then
1396 ** return 0.
1397 */
1398 static int timeline_endpoint(
1399 int iFrom, /* Starting point */
1400 const char *zEnd, /* Tag we are searching for */
1401 int bForward /* 1: forwards in time (descendants) 0: backwards */
1402 ){
1403 int tagId;
1404 int endId = 0;
1405 Stmt q;
@@ -1590,11 +1684,11 @@
1590 ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2'
1591 ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
1592 ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN
1593 ** p=CX ... from CX back to time of CHECKIN
1594 ** from=CX ... path from CX back to CHECKIN
1595 ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
1596 ** d=CX ... from CX up to the time of CHECKIN
1597 ** from=CX ... path from CX up to CHECKIN
1598 ** t=TAG Show only check-ins with the given TAG
1599 ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
1600 ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
@@ -1607,11 +1701,11 @@
1607 ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
1608 ** "like", or "regexp".
1609 ** u=USER Only show items associated with USER
1610 ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
1611 ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1612 * x: "Classic".
1613 ** advm Use the "Advanced" or "Busy" menu design.
1614 ** ng No Graph.
1615 ** ncp Omit cherrypick merges
1616 ** nd Do not highlight the focus check-in
1617 ** nsm Omit the submenu
@@ -1830,10 +1924,13 @@
1830 || (bisectLocal && !g.perm.Setup)
1831 ){
1832 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1833 return;
1834 }
 
 
 
1835 if( !bisectLocal ){
1836 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
1837 }
1838 cookie_read_parameter("y","y");
1839 zType = P("y");
@@ -1969,10 +2066,11 @@
1969 }
1970 if( showSql ) db_append_dml_to_blob(&allSql);
1971 if( zUses!=0 ){
1972 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
1973 if( ufid ){
 
1974 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
1975 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
1976 compute_uses_file("usesfile", ufid, 0);
1977 zType = "ci";
1978 disableY = 1;
@@ -2042,11 +2140,11 @@
2042 zBisect = 0;
2043 }
2044
2045 style_header("Timeline");
2046 if( advancedMenu ){
2047 style_submenu_element("Help", "%R/help?cmd=/timeline");
2048 }
2049 login_anonymous_available();
2050 timeline_temp_table();
2051 blob_zero(&sql);
2052 blob_zero(&desc);
@@ -2283,11 +2381,11 @@
2283 if( zError==0 ){
2284 zError = "Cannot use the ft= query parameter when both p= and d= "
2285 "are used and have distinct values.";
2286 }
2287 zFwdTo = 0;
2288 }
2289 if( zFwdTo ){
2290 double rStartDate = mtime_of_rid(d_rid, 0.0);
2291 ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
2292 if( ridFwdTo==0 ){
2293 ridFwdTo = name_to_typed_rid(zBackTo,"ci");
@@ -2342,11 +2440,11 @@
2342 if( zError==0 ){
2343 zError = "Cannot use the bt= query parameter when both p= and d= "
2344 "are used and have distinct values.";
2345 }
2346 zBackTo = 0;
2347 }
2348 if( zBackTo ){
2349 double rDateLimit = mtime_of_rid(p_rid, 0.0);
2350 ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
2351 if( ridBackTo==0 ){
2352 ridBackTo = name_to_typed_rid(zBackTo,"ci");
@@ -2368,11 +2466,11 @@
2368 }else{
2369 removeFileGlobFromOk(zChng);
2370 np = db_int(0, "SELECT count(*)-1 FROM ok");
2371 if( np>0 || nd==0 ){
2372 if( nd>0 ) blob_appendf(&desc, " and ");
2373 blob_appendf(&desc, "%d ancestor%s",
2374 np>=0 ? np : 0, (1==np)?"":"s");
2375 db_multi_exec("%s", blob_sql_text(&sql));
2376 }
2377 if( useDividers && !selectedRid ) selectedRid = p_rid;
2378 }
@@ -2402,11 +2500,11 @@
2402 href("%R/info?name=%h",zBaseName), zBaseName,
2403 href("%R/info?name=%h",zBackTo), zBackTo);
2404 }else{
2405 blob_appendf(&desc, " back to %z%h</a>%s",
2406 href("%R/info?name=%h",zBackTo), zBackTo,
2407 bBackAdded ? " (not a direct anscestor)" : "");
2408 if( ridFwdTo && zFwdTo ){
2409 blob_appendf(&desc, " and up to %z%h</a>%s",
2410 href("%R/info?name=%h",zFwdTo), zFwdTo,
2411 bFwdAdded ? " (not a direct descendant)" : "");
2412 }
@@ -2651,11 +2749,11 @@
2651 blob_append_sql(&cond,
2652 " AND event.mtime>=julianday(%Q,%Q)"
2653 " AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
2654 zStart, zTZMod, zEnd, zTZMod);
2655 nEntry = -1;
2656
2657 if( fossil_ui_localtime() && bZulu ){
2658 zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
2659 }else{
2660 zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
2661 }
@@ -3107,10 +3205,11 @@
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*/");
 
3112 }else{
3113 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
3114 }
3115 if( fossil_islower(desc.aData[0]) ){
3116 desc.aData[0] = fossil_toupper(desc.aData[0]);
@@ -3139,10 +3238,20 @@
3139
3140 /* Report any errors. */
3141 if( zError ){
3142 @ <p class="generalError">%h(zError)</p>
3143 }
 
 
 
 
 
 
 
 
 
 
3144
3145 if( zNewerButton ){
3146 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
3147 @ &nbsp;&uarr;</a>
3148 }
@@ -3620,19 +3729,21 @@
3620 ** N is negative, output the first -N lines. If N is
3621 ** zero, no limit. Default is -20 meaning 20 lines.
3622 ** --offset P Skip P changes
3623 ** -p|--path PATH Output items affecting PATH only.
3624 ** PATH can be a file or a sub directory.
 
3625 ** -R REPO_FILE Specifies the repository db to use. Default is
3626 ** the current check-out's repository.
3627 ** --sql Show the SQL used to generate the timeline
3628 ** -t|--type TYPE Output items from the given types only, such as:
3629 ** ci = file commits only
3630 ** e = technical notes only
3631 ** f = forum posts only
3632 ** t = tickets only
3633 ** w = wiki commits only
 
3634 ** -v|--verbose Output the list of files changed by each commit
3635 ** and the type of each change (edited, deleted,
3636 ** etc.) after the check-in comment.
3637 ** -W|--width N Width of lines (default is to auto-detect). N must be
3638 ** either greater than 20 or it must be zero 0 to
@@ -3644,17 +3755,19 @@
3644 int n, k, width;
3645 const char *zLimit;
3646 const char *zWidth;
3647 const char *zOffset;
3648 const char *zType;
 
3649 char *zOrigin;
3650 char *zDate;
3651 Blob sql;
3652 int objid = 0;
3653 Blob uuid;
3654 int mode = TIMELINE_MODE_NONE;
3655 int verboseFlag = 0 ;
 
3656 int iOffset;
3657 const char *zFilePattern = 0;
3658 const char *zFormat = 0;
3659 const char *zBr = 0;
3660 Blob treeName;
@@ -3666,10 +3779,11 @@
3666 }
3667 db_find_and_open_repository(0, 0);
3668 zLimit = find_option("limit","n",1);
3669 zWidth = find_option("width","W",1);
3670 zType = find_option("type","t",1);
 
3671 zFilePattern = find_option("path","p",1);
3672 zFormat = find_option("format","F",1);
3673 zBr = find_option("branch","b",1);
3674 if( find_option("current-branch","c",0)!=0 ){
3675 if( !g.localOpen ){
@@ -3707,10 +3821,11 @@
3707 }else{
3708 width = -1;
3709 }
3710 zOffset = find_option("offset",0,1);
3711 iOffset = zOffset ? atoi(zOffset) : 0;
 
3712
3713 /* We should be done with options.. */
3714 verify_all_options();
3715
3716 if( g.argc>=4 ){
@@ -3727,11 +3842,11 @@
3727 mode = TIMELINE_MODE_PARENTS;
3728 }else if( strncmp(g.argv[2],"parents",k)==0 ){
3729 mode = TIMELINE_MODE_PARENTS;
3730 }else if(!zType && !zLimit){
3731 usage("?WHEN? ?CHECKIN|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
3732 "?-W|--width WIDTH? ?-p|--path PATH?");
3733 }
3734 if( '-' != *g.argv[3] ){
3735 zOrigin = g.argv[3];
3736 }else{
3737 zOrigin = "now";
@@ -3797,10 +3912,13 @@
3797 mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
3798 );
3799 if( zType && (zType[0]!='a') ){
3800 blob_append_sql(&sql, "\n AND event.type=%Q ", zType);
3801 }
 
 
 
3802
3803 /* When zFilePattern is specified, compute complete ancestry;
3804 * limit later at print_timeline() */
3805 if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
3806 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
@@ -3850,23 +3968,25 @@
3850 " WHERE tx.value='%q'\n"
3851 ")\n" /* No merge closures */
3852 " AND (tagxref.value IS NULL OR tagxref.value='%q')",
3853 zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
3854 }
3855
3856 if( mode==TIMELINE_MODE_AFTER ){
3857 int lim = n;
3858 if( n == 0 ){
3859 lim = -1; /* 0 means no limit */
3860 }else if( n < 0 ){
3861 lim = -n;
3862 }
3863 /* Complete the above outer select. */
3864 blob_append_sql(&sql,
3865 "\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime DESC;", lim);
 
3866 }else{
3867 blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
 
3868 }
3869 if( iOffset>0 ){
3870 /* Don't handle LIMIT here, otherwise print_timeline()
3871 * will not determine the end-marker correctly! */
3872 blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
3873
--- src/timeline.c
+++ src/timeline.c
@@ -115,11 +115,13 @@
115 #define TIMELINE_COMPACT 0x0001000 /* Use the "compact" view style */
116 #define TIMELINE_VERBOSE 0x0002000 /* Use the "detailed" view style */
117 #define TIMELINE_MODERN 0x0004000 /* Use the "modern" view style */
118 #define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */
119 #define TIMELINE_CLASSIC 0x0010000 /* Use the "classic" view style */
120 #define TIMELINE_SIMPLE 0x0020000 /* Use the "simple" view style */
121 #define TIMELINE_INLINE 0x0033000 /* Mask for views with in-line display */
122 #define TIMELINE_VIEWS 0x003f000 /* Mask for all of the view styles */
123 #define TIMELINE_NOSCROLL 0x0100000 /* Don't scroll to the selection */
124 #define TIMELINE_FILEDIFF 0x0200000 /* Show File differences, not ckin diffs */
125 #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */
126 #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */
127 #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */
@@ -170,10 +172,182 @@
172 manifest_destroy(pPost);
173 }
174 }
175
176
177 /*
178 ** This routine generates the default "extra" text after the description
179 ** in a timeline.
180 **
181 ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
182 **
183 ** This routine is used if no xExtra argument is supplied to
184 ** www_print_timeline().
185 */
186 void timeline_extra(
187 Stmt *pQuery, /* Current row of the timeline query */
188 int tmFlags, /* Flags to www_print_timeline() */
189 const char *zThisUser, /* Suppress links to this user */
190 const char *zThisTag /* Suppress links to this tag */
191 ){
192 int rid = db_column_int(pQuery, 0);
193 const char *zUuid = db_column_text(pQuery, 1);
194 const char *zDate = db_column_text(pQuery, 2);
195 const char *zType = db_column_text(pQuery, 7);
196 const char *zUser = db_column_text(pQuery, 4);
197 const char *zTagList = db_column_text(pQuery, 8);
198 int tagid = db_column_int(pQuery, 9);
199 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
200
201 if( (tmFlags & TIMELINE_INLINE)!=0 ){
202 cgi_printf("(");
203 }
204
205 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
206 if( zType[0]=='c' ){
207 const char *zPrefix = 0;
208 static int markLeaves = -1;
209 if( markLeaves<0 ){
210 markLeaves = db_get_int("timeline-mark-leaves",1);
211 if( markLeaves<0 ) markLeaves = 1;
212 }
213 if( strcmp(zUuid, MANIFEST_UUID)==0 ){
214 /* This will only ever happen when Fossil is drawing a timeline for
215 ** its own self-host repository. If the timeline shows the specific
216 ** check-in corresponding to the current executable, then tag that
217 ** check-in with "self" */
218 zPrefix = "self&nbsp;";
219 }else if( markLeaves && db_column_int(pQuery,5) ){
220 if( markLeaves==1 ){
221 zPrefix = has_closed_tag(rid) ? "closed&nbsp;" : "leaf&nbsp;";
222 }else{
223 zPrefix = has_closed_tag(rid) ?
224 "<span class='timelineLeaf'>Closed-Leaf</span>\n" :
225 "<span class='timelineLeaf'>Leaf</span>\n";
226 }
227 }
228 cgi_printf("%scheck-in:&nbsp;%z<span class='timelineHash'>"
229 "%S</span></a> ",
230 zPrefix, href("%R/info/%!S",zUuid),zUuid);
231 }else if( zType[0]=='e' && tagid ){
232 cgi_printf("technote:&nbsp;");
233 hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
234 }else{
235 cgi_printf("artifact:&nbsp;%z%S</a> ",
236 href("%R/info/%!S",zUuid),zUuid);
237 }
238 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
239 || zType[0]=='n' || zType[0]=='f'){
240 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
241 }
242
243 if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
244 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
245 @ data-id='%d(rid)'>...</span>
246 @ <span class='clutter' id='detail-%d(rid)'>
247 }
248
249 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
250 char *zLink;
251 if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
252 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
253 }else{
254 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
255 }
256 cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
257 }else{
258 cgi_printf("user:&nbsp;%h", zDispUser);
259 }
260
261 /* Generate the "tags: TAGLIST" at the end of the comment, together
262 ** with hyperlinks to the tag list.
263 */
264 if( zTagList && zTagList[0]==0 ) zTagList = 0;
265 if( zTagList ){
266 if( g.perm.Hyperlink ){
267 int i;
268 const char *z = zTagList;
269 Blob links;
270 blob_zero(&links);
271 while( z && z[0] ){
272 for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
273 if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
274 blob_appendf(&links,
275 "%z%#h</a>%.2s",
276 href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
277 );
278 }else{
279 blob_appendf(&links, "%#h", i+2, z);
280 }
281 if( z[i]==0 ) break;
282 z += i+2;
283 }
284 cgi_printf(" tags:&nbsp;%s", blob_str(&links));
285 blob_reset(&links);
286 }else{
287 cgi_printf(" tags:&nbsp;%h", zTagList);
288 }
289 }
290
291 if( tmFlags & TIMELINE_SHOWRID ){
292 int srcId = delta_source_rid(rid);
293 if( srcId ){
294 cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
295 href("%R/deltachain/%d",rid), rid, srcId);
296 }else{
297 cgi_printf(" id:&nbsp;%z%d</a>",
298 href("%R/deltachain/%d",rid), rid);
299 }
300 }
301 tag_private_status(rid);
302
303 if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
304 cgi_printf("</span>"); /* End of the declutter span */
305 }
306
307 /* End timelineDetail */
308 if( (tmFlags & TIMELINE_INLINE)!=0 ){
309 cgi_printf(")");
310 }
311 }
312
313
314 /*
315 ** SETTING: timeline-truncate-at-blank boolean default=off
316 **
317 ** If enabled, check-in comments displayed on the timeline are truncated
318 ** at the first blank line of the comment text. The comment text after
319 ** the first blank line is only seen in the /info or similar pages that
320 ** show details about the check-in.
321 */
322 /*
323 ** SETTING: timeline-tslink-info boolean default=off
324 **
325 ** The hyperlink on the timestamp associated with each timeline entry,
326 ** on the far left-hand side of the screen, normally targets another
327 ** /timeline page that shows the entry in context. However, if this
328 ** option is turned on, that hyperlink targets the /info page showing
329 ** the details of the entry.
330 */
331 /*
332 ** SETTING: timeline-mark-leaves width=5 default=1
333 **
334 ** Determine whether or not leaf check-ins are marked as such in the
335 ** details section of the timeline. The value is an integer between 0
336 ** and 2:
337 **
338 ** 0 Do not show any special marking for leaf check-ins.
339 **
340 ** 1 Show just "leaf" or "closed"
341 **
342 ** 2 Show "Leaf" or "Closed-Leaf" with emphasis
343 **
344 ** The default is currently 1. Prior to 2025-10-19, the default was 2.
345 ** This setting has no effect on the "Classic" view, which always behaves
346 ** as if the setting were 2.
347 */
348
349 /*
350 ** Output a timeline in the web format given a query. The query
351 ** should return these columns:
352 **
353 ** 0. rid
@@ -194,11 +368,11 @@
368 const char *zThisUser, /* Suppress links to this user */
369 const char *zThisTag, /* Suppress links to this tag */
370 Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
371 int selectedRid, /* Highlight the line with this RID value or zero */
372 int secondRid, /* Secondary highlight (or zero) */
373 void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
374 ){
375 int mxWikiLen;
376 Blob comment;
377 int prevTagid = 0;
378 int suppressCnt = 0;
@@ -214,55 +388,41 @@
388 const char *zStyle; /* Sub-name for classes for the style */
389 const char *zDateFmt;
390 int iTableId = timeline_tableid();
391 int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
392 ** page rather than the /timeline page */
393 char *zMainBranch = db_get("main-branch","trunk");
394
395
396 if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
397 vid = db_lget_int("checkout", 0);
398 }
399 if( xExtra==0 ) xExtra = timeline_extra;
400 zPrevDate[0] = 0;
401 mxWikiLen = db_get_int("timeline-max-comment", 0);
402 dateFormat = db_get_int("timeline-date-format", 0);
 
 
 
 
 
 
 
 
403 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
 
 
 
 
 
 
 
 
 
404 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
405 if( (tmFlags & TIMELINE_VIEWS)==0 ){
406 tmFlags |= timeline_ss_cookie();
407 }
408 if( tmFlags & TIMELINE_COLUMNAR ){
409 zStyle = "Columnar";
410 }else if( tmFlags & TIMELINE_COMPACT ){
411 zStyle = "Compact";
412 }else if( tmFlags & TIMELINE_SIMPLE ){
413 zStyle = "Simple";
414 }else if( tmFlags & TIMELINE_VERBOSE ){
415 zStyle = "Verbose";
416 }else if( tmFlags & TIMELINE_CLASSIC ){
417 zStyle = "Classic";
418 }else{
419 zStyle = "Modern";
420 }
421 zDateFmt = P("datefmt");
422 if( zDateFmt ) dateFormat = atoi(zDateFmt);
423 pGraph = graph_init();
 
 
424 if( (tmFlags & TIMELINE_CHPICK)!=0
425 && !db_table_exists("repository","cherrypick")
426 ){
427 tmFlags &= ~TIMELINE_CHPICK;
428 }
@@ -275,13 +435,11 @@
435 int isLeaf = db_column_int(pQuery, 5);
436 const char *zBgClr = db_column_text(pQuery, 6);
437 const char *zDate = db_column_text(pQuery, 2);
438 const char *zType = db_column_text(pQuery, 7);
439 const char *zUser = db_column_text(pQuery, 4);
 
440 int tagid = db_column_int(pQuery, 9);
 
441 char *zBr = 0; /* Branch */
442 int commentColumn = 3; /* Column containing comment text */
443 int modPending; /* Pending moderation */
444 char *zDateLink; /* URL for the link on the timestamp */
445 int drawDetailEllipsis; /* True to show ellipsis in place of detail */
@@ -434,11 +592,11 @@
592 zBr = branch_of_rid(rid);
593 if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
594 /* If no background color is specified, use a color based on the
595 ** branch name */
596 if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
597 }else if( zBr==0 || strcmp(zBr,zMainBranch)==0 ){
598 zBgClr = 0;
599 }else{
600 zBgClr = hash_color(zBr);
601 }
602 }
@@ -446,32 +604,34 @@
604 if( zType[0]=='c' && pGraph ){
605 int nParent = 0;
606 int nCherrypick = 0;
607 GraphRowId aParent[GR_MAX_RAIL];
608 static Stmt qparent;
609 if( tmFlags & TIMELINE_GRAPH ){
610 db_static_prepare(&qparent,
611 "SELECT pid FROM plink"
612 " WHERE cid=:rid AND pid NOT IN phantom"
613 " ORDER BY isprim DESC /*sort*/"
614 );
615 db_bind_int(&qparent, ":rid", rid);
616 while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
617 aParent[nParent++] = db_column_int(&qparent, 0);
618 }
619 db_reset(&qparent);
620 if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){
621 static Stmt qcherrypick;
622 db_static_prepare(&qcherrypick,
623 "SELECT parentid FROM cherrypick"
624 " WHERE childid=:rid AND parentid NOT IN phantom"
625 );
626 db_bind_int(&qcherrypick, ":rid", rid);
627 while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
628 aParent[nParent++] = db_column_int(&qcherrypick, 0);
629 nCherrypick++;
630 }
631 db_reset(&qcherrypick);
632 }
633 }
634 gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
635 zBr, zBgClr, zUuid,
636 isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
637 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
@@ -598,23 +758,15 @@
758 drawDetailEllipsis = 0;
759 }else{
760 cgi_printf("%W",blob_str(&comment));
761 }
762 }
 
 
 
 
 
 
 
 
763
764 @ </span>
765 blob_reset(&comment);
766
767 /* Generate extra information and hyperlinks that follow the comment.
768 ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
769 */
770 if( drawDetailEllipsis ){
771 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
772 @ data-id='%d(rid)'>...</span>
@@ -628,101 +780,17 @@
780 }
781 if( tmFlags & TIMELINE_COMPACT ){
782 cgi_printf("<span class='clutter' id='detail-%d'>",rid);
783 }
784 cgi_printf("<span class='timeline%sDetail'>", zStyle);
785 xExtra(pQuery, tmFlags, zThisUser, zThisTag);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786 if( tmFlags & TIMELINE_COMPACT ){
787 @ </span></span>
788 }else{
789 @ </span>
790 }
791
792 /* Generate the file-change list if requested */
793 if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
794 && zType[0]=='c' && g.perm.Hyperlink
795 ){
796 int inUl = 0;
@@ -908,11 +976,13 @@
976 int omitDescenders; /* True to omit descenders */
977 int scrollToSelect; /* True to scroll to the selection */
978 int dwellTimeout; /* Milliseconds to wait for tooltips to show */
979 int closeTimeout; /* Milliseconds to wait for tooltips to close */
980 u8 *aiMap; /* The rail map */
981 u8 bNoGraph; /* True to show a minimal graph */
982
983 bNoGraph = (tmFlags & TIMELINE_GRAPH)==0;
984 iRailPitch = atoi(PD("railpitch","0"));
985 showArrowheads = skin_detail_boolean("timeline-arrowheads");
986 circleNodes = skin_detail_boolean("timeline-circle-nodes");
987 colorGraph = skin_detail_boolean("timeline-color-graph-lines");
988 iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
@@ -930,11 +1000,11 @@
1000 @ "nomo": %d(PB("nomo")),
1001 @ "iTopRow": %d(iTopRow),
1002 @ "omitDescenders": %d(omitDescenders),
1003 @ "fileDiff": %d(fileDiff),
1004 @ "scrollToSelect": %d(scrollToSelect),
1005 @ "nrail": %d(bNoGraph?1:pGraph->mxRail+1),
1006 @ "baseUrl": "%R",
1007 @ "dwellTimeout": %d(dwellTimeout),
1008 @ "closeTimeout": %d(closeTimeout),
1009 @ "hashDigits": %d(hash_digits(1)),
1010 @ "bottomRowId": "btm-%d(iTableId)",
@@ -992,12 +1062,16 @@
1062 aiMap = pGraph->aiRailMap;
1063 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
1064 int k = 0;
1065 cgi_printf("{\"id\":%d,", pRow->idx);
1066 cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
1067 if( bNoGraph ){
1068 cgi_printf("\"r\":0,"); /* Chng to ":-1" to omit node circles */
1069 }else{
1070 cgi_printf("\"r\":%d,", pRow->iRail>=0 ? aiMap[pRow->iRail] : -1);
1071 }
1072 if( pRow->bDescender && !bNoGraph ){
1073 cgi_printf("\"d\":%d,", pRow->bDescender);
1074 }
1075 if( pRow->mergeOut>=0 ){
1076 cgi_printf("\"mo\":%d,", aiMap[pRow->mergeOut]);
1077 if( pRow->mergeUpto==0 ) pRow->mergeUpto = pRow->idx;
@@ -1004,11 +1078,13 @@
1078 cgi_printf("\"mu\":%d,", pRow->mergeUpto);
1079 if( pRow->cherrypickUpto>0 && pRow->cherrypickUpto<=pRow->mergeUpto ){
1080 cgi_printf("\"cu\":%d,", pRow->cherrypickUpto);
1081 }
1082 }
1083 if( bNoGraph ){
1084 cgi_printf("\"u\":-1,");
1085 }else if( pRow->isStepParent ){
1086 cgi_printf("\"sb\":%d,", pRow->aiRiser[pRow->iRail]);
1087 }else{
1088 cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
1089 }
1090 k = 0;
@@ -1206,10 +1282,26 @@
1282 if( i>2 ){
1283 style_submenu_multichoice("y", i/2, az, isDisabled);
1284 }
1285 }
1286
1287 /*
1288 ** SETTING: timeline-default-style width=5 default=m
1289 **
1290 ** This setting determines the default "view style" for timelines.
1291 ** The setting should be a single character, one of the following:
1292 **
1293 ** c Compact
1294 ** j Columnar
1295 ** m Modern
1296 ** s Simple
1297 ** v Verbose
1298 ** x Classic
1299 **
1300 ** The default value is m (Modern).
1301 */
1302
1303 /*
1304 ** Return the default value for the "ss" cookie or query parameter.
1305 ** The "ss" cookie determines the graph style. See the
1306 ** timeline_view_styles[] global constant for a list of choices.
1307 */
@@ -1230,10 +1322,11 @@
1322 switch( v[0] ){
1323 case 'c': tmFlags = TIMELINE_COMPACT; break;
1324 case 'v': tmFlags = TIMELINE_VERBOSE; break;
1325 case 'j': tmFlags = TIMELINE_COLUMNAR; break;
1326 case 'x': tmFlags = TIMELINE_CLASSIC; break;
1327 case 's': tmFlags = TIMELINE_SIMPLE; break;
1328 default: tmFlags = TIMELINE_MODERN; break;
1329 }
1330 return tmFlags;
1331 }
1332
@@ -1242,15 +1335,16 @@
1335 */
1336 const char *const timeline_view_styles[] = {
1337 "m", "Modern View",
1338 "j", "Columnar View",
1339 "c", "Compact View",
1340 "s", "Simple View",
1341 "v", "Verbose View",
1342 "x", "Classic View",
1343 };
1344 #if INTERFACE
1345 # define N_TIMELINE_VIEW_STYLE 6
1346 #endif
1347
1348 /*
1349 ** Add the select/option box to the timeline submenu that is used to
1350 ** set the ss= parameter that determines the viewing mode.
@@ -1363,11 +1457,11 @@
1457 ** the input is a valid date space and false if not.
1458 */
1459 static int timeline_is_datespan(const char *zDay){
1460 size_t n = strlen(zDay);
1461 int i, d, m;
1462
1463 if( n<17 || n>18 ) return 0;
1464 if( n==18 ){
1465 if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
1466 n--;
1467 }
@@ -1389,17 +1483,17 @@
1483
1484 /*
1485 ** Find the first check-in encountered with a particular tag
1486 ** when moving either forwards are backwards in time from a
1487 ** particular starting point (iFrom). Return the rid of that
1488 ** first check-in. If there are no check-ins in the descendent
1489 ** or ancestor set of check-in iFrom that match the tag, then
1490 ** return 0.
1491 */
1492 static int timeline_endpoint(
1493 int iFrom, /* Starting point */
1494 const char *zEnd, /* Tag we are searching for */
1495 int bForward /* 1: forwards in time (descendants) 0: backwards */
1496 ){
1497 int tagId;
1498 int endId = 0;
1499 Stmt q;
@@ -1590,11 +1684,11 @@
1684 ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2'
1685 ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
1686 ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN
1687 ** p=CX ... from CX back to time of CHECKIN
1688 ** from=CX ... path from CX back to CHECKIN
1689 ** ft=CHECKIN "Forward To": Show descendents forward to CHECKIN
1690 ** d=CX ... from CX up to the time of CHECKIN
1691 ** from=CX ... path from CX up to CHECKIN
1692 ** t=TAG Show only check-ins with the given TAG
1693 ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
1694 ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
@@ -1607,11 +1701,11 @@
1701 ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
1702 ** "like", or "regexp".
1703 ** u=USER Only show items associated with USER
1704 ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
1705 ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1706 ** x: "Classic".
1707 ** advm Use the "Advanced" or "Busy" menu design.
1708 ** ng No Graph.
1709 ** ncp Omit cherrypick merges
1710 ** nd Do not highlight the focus check-in
1711 ** nsm Omit the submenu
@@ -1830,10 +1924,13 @@
1924 || (bisectLocal && !g.perm.Setup)
1925 ){
1926 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1927 return;
1928 }
1929 if( zBefore || zCirca ){
1930 if( robot_restrict("timelineX") ) return;
1931 }
1932 if( !bisectLocal ){
1933 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
1934 }
1935 cookie_read_parameter("y","y");
1936 zType = P("y");
@@ -1969,10 +2066,11 @@
2066 }
2067 if( showSql ) db_append_dml_to_blob(&allSql);
2068 if( zUses!=0 ){
2069 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
2070 if( ufid ){
2071 if( robot_restrict("timelineX") ) return;
2072 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
2073 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
2074 compute_uses_file("usesfile", ufid, 0);
2075 zType = "ci";
2076 disableY = 1;
@@ -2042,11 +2140,11 @@
2140 zBisect = 0;
2141 }
2142
2143 style_header("Timeline");
2144 if( advancedMenu ){
2145 style_submenu_element("Help", "%R/help/www/timeline");
2146 }
2147 login_anonymous_available();
2148 timeline_temp_table();
2149 blob_zero(&sql);
2150 blob_zero(&desc);
@@ -2283,11 +2381,11 @@
2381 if( zError==0 ){
2382 zError = "Cannot use the ft= query parameter when both p= and d= "
2383 "are used and have distinct values.";
2384 }
2385 zFwdTo = 0;
2386 }
2387 if( zFwdTo ){
2388 double rStartDate = mtime_of_rid(d_rid, 0.0);
2389 ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
2390 if( ridFwdTo==0 ){
2391 ridFwdTo = name_to_typed_rid(zBackTo,"ci");
@@ -2342,11 +2440,11 @@
2440 if( zError==0 ){
2441 zError = "Cannot use the bt= query parameter when both p= and d= "
2442 "are used and have distinct values.";
2443 }
2444 zBackTo = 0;
2445 }
2446 if( zBackTo ){
2447 double rDateLimit = mtime_of_rid(p_rid, 0.0);
2448 ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
2449 if( ridBackTo==0 ){
2450 ridBackTo = name_to_typed_rid(zBackTo,"ci");
@@ -2368,11 +2466,11 @@
2466 }else{
2467 removeFileGlobFromOk(zChng);
2468 np = db_int(0, "SELECT count(*)-1 FROM ok");
2469 if( np>0 || nd==0 ){
2470 if( nd>0 ) blob_appendf(&desc, " and ");
2471 blob_appendf(&desc, "%d ancestor%s",
2472 np>=0 ? np : 0, (1==np)?"":"s");
2473 db_multi_exec("%s", blob_sql_text(&sql));
2474 }
2475 if( useDividers && !selectedRid ) selectedRid = p_rid;
2476 }
@@ -2402,11 +2500,11 @@
2500 href("%R/info?name=%h",zBaseName), zBaseName,
2501 href("%R/info?name=%h",zBackTo), zBackTo);
2502 }else{
2503 blob_appendf(&desc, " back to %z%h</a>%s",
2504 href("%R/info?name=%h",zBackTo), zBackTo,
2505 bBackAdded ? " (not a direct ancestor)" : "");
2506 if( ridFwdTo && zFwdTo ){
2507 blob_appendf(&desc, " and up to %z%h</a>%s",
2508 href("%R/info?name=%h",zFwdTo), zFwdTo,
2509 bFwdAdded ? " (not a direct descendant)" : "");
2510 }
@@ -2651,11 +2749,11 @@
2749 blob_append_sql(&cond,
2750 " AND event.mtime>=julianday(%Q,%Q)"
2751 " AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
2752 zStart, zTZMod, zEnd, zTZMod);
2753 nEntry = -1;
2754
2755 if( fossil_ui_localtime() && bZulu ){
2756 zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
2757 }else{
2758 zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
2759 }
@@ -3107,10 +3205,11 @@
3205 if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
3206 }
3207 blob_zero(&sql);
3208 if( PB("oldestfirst") ){
3209 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
3210 tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK);
3211 }else{
3212 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
3213 }
3214 if( fossil_islower(desc.aData[0]) ){
3215 desc.aData[0] = fossil_toupper(desc.aData[0]);
@@ -3139,10 +3238,20 @@
3238
3239 /* Report any errors. */
3240 if( zError ){
3241 @ <p class="generalError">%h(zError)</p>
3242 }
3243
3244 /* Swap zNewer and zOlder buttons if we display oldestfirst */
3245 if( PB("oldestfirst") ){
3246 char *zSwap = zNewerButton;
3247 char *zSwapLabel = zNewerButtonLabel;
3248 zNewerButton = zOlderButton;
3249 zNewerButtonLabel = zOlderButtonLabel;
3250 zOlderButton = zSwap;
3251 zOlderButtonLabel = zSwapLabel;
3252 }
3253
3254 if( zNewerButton ){
3255 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
3256 @ &nbsp;&uarr;</a>
3257 }
@@ -3620,19 +3729,21 @@
3729 ** N is negative, output the first -N lines. If N is
3730 ** zero, no limit. Default is -20 meaning 20 lines.
3731 ** --offset P Skip P changes
3732 ** -p|--path PATH Output items affecting PATH only.
3733 ** PATH can be a file or a sub directory.
3734 ** -r|--reverse Show items in chronological order.
3735 ** -R REPO_FILE Specifies the repository db to use. Default is
3736 ** the current check-out's repository.
3737 ** --sql Show the SQL used to generate the timeline
3738 ** -t|--type TYPE Output items from the given types only, such as:
3739 ** ci = file commits only
3740 ** e = technical notes only
3741 ** f = forum posts only
3742 ** t = tickets only
3743 ** w = wiki commits only
3744 ** -u|--for-user USER Only show items associated with USER
3745 ** -v|--verbose Output the list of files changed by each commit
3746 ** and the type of each change (edited, deleted,
3747 ** etc.) after the check-in comment.
3748 ** -W|--width N Width of lines (default is to auto-detect). N must be
3749 ** either greater than 20 or it must be zero 0 to
@@ -3644,17 +3755,19 @@
3755 int n, k, width;
3756 const char *zLimit;
3757 const char *zWidth;
3758 const char *zOffset;
3759 const char *zType;
3760 const char *zUser;
3761 char *zOrigin;
3762 char *zDate;
3763 Blob sql;
3764 int objid = 0;
3765 Blob uuid;
3766 int mode = TIMELINE_MODE_NONE;
3767 int verboseFlag = 0;
3768 int reverseFlag = 0;
3769 int iOffset;
3770 const char *zFilePattern = 0;
3771 const char *zFormat = 0;
3772 const char *zBr = 0;
3773 Blob treeName;
@@ -3666,10 +3779,11 @@
3779 }
3780 db_find_and_open_repository(0, 0);
3781 zLimit = find_option("limit","n",1);
3782 zWidth = find_option("width","W",1);
3783 zType = find_option("type","t",1);
3784 zUser = find_option("for-user","u",1);
3785 zFilePattern = find_option("path","p",1);
3786 zFormat = find_option("format","F",1);
3787 zBr = find_option("branch","b",1);
3788 if( find_option("current-branch","c",0)!=0 ){
3789 if( !g.localOpen ){
@@ -3707,10 +3821,11 @@
3821 }else{
3822 width = -1;
3823 }
3824 zOffset = find_option("offset",0,1);
3825 iOffset = zOffset ? atoi(zOffset) : 0;
3826 reverseFlag = find_option("reverse","r",0)!=0;
3827
3828 /* We should be done with options.. */
3829 verify_all_options();
3830
3831 if( g.argc>=4 ){
@@ -3727,11 +3842,11 @@
3842 mode = TIMELINE_MODE_PARENTS;
3843 }else if( strncmp(g.argv[2],"parents",k)==0 ){
3844 mode = TIMELINE_MODE_PARENTS;
3845 }else if(!zType && !zLimit){
3846 usage("?WHEN? ?CHECKIN|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
3847 "?-W|--width WIDTH? ?-p|--path PATH? ?-r|--reverse?");
3848 }
3849 if( '-' != *g.argv[3] ){
3850 zOrigin = g.argv[3];
3851 }else{
3852 zOrigin = "now";
@@ -3797,10 +3912,13 @@
3912 mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
3913 );
3914 if( zType && (zType[0]!='a') ){
3915 blob_append_sql(&sql, "\n AND event.type=%Q ", zType);
3916 }
3917 if( zUser && (zUser[0]!='\0') ){
3918 blob_append_sql(&sql, "\n AND user0=%Q ", zUser);
3919 }
3920
3921 /* When zFilePattern is specified, compute complete ancestry;
3922 * limit later at print_timeline() */
3923 if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
3924 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
@@ -3850,23 +3968,25 @@
3968 " WHERE tx.value='%q'\n"
3969 ")\n" /* No merge closures */
3970 " AND (tagxref.value IS NULL OR tagxref.value='%q')",
3971 zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
3972 }
3973
3974 if( mode==TIMELINE_MODE_AFTER ){
3975 int lim = n;
3976 if( n == 0 ){
3977 lim = -1; /* 0 means no limit */
3978 }else if( n < 0 ){
3979 lim = -n;
3980 }
3981 /* Complete the above outer select. */
3982 blob_append_sql(&sql,
3983 "\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime %s",
3984 lim, reverseFlag ? "" : "DESC");
3985 }else{
3986 blob_append_sql(&sql,
3987 "\nORDER BY event.mtime %s", reverseFlag ? "" : "DESC");
3988 }
3989 if( iOffset>0 ){
3990 /* Don't handle LIMIT here, otherwise print_timeline()
3991 * will not determine the end-marker correctly! */
3992 blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
3993
+4
--- src/tkt.c
+++ src/tkt.c
@@ -791,10 +791,14 @@
791791
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
792792
793793
if( zFullName ){
794794
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
795795
}
796
+
797
+ builtin_fossil_js_bundle_or("dom", "storage", NULL);
798
+ builtin_request_js("fossil.page.ticket.js");
799
+ builtin_fulfill_js_requests();
796800
797801
style_finish_page();
798802
}
799803
800804
/*
801805
--- src/tkt.c
+++ src/tkt.c
@@ -791,10 +791,14 @@
791 if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
792
793 if( zFullName ){
794 attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
795 }
 
 
 
 
796
797 style_finish_page();
798 }
799
800 /*
801
--- src/tkt.c
+++ src/tkt.c
@@ -791,10 +791,14 @@
791 if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
792
793 if( zFullName ){
794 attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
795 }
796
797 builtin_fossil_js_bundle_or("dom", "storage", NULL);
798 builtin_request_js("fossil.page.ticket.js");
799 builtin_fulfill_js_requests();
800
801 style_finish_page();
802 }
803
804 /*
805
+12 -8
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -603,23 +603,24 @@
603603
@ }
604604
@ }
605605
@ }
606606
@ set seenRow 0
607607
@ set alwaysPlaintext [info exists plaintext]
608
-@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
608
+@ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
609609
@ mimetype as xmimetype, icomment AS xcomment,
610610
@ username AS xusername
611611
@ FROM ticketchng
612612
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
613613
@ if {$seenRow} {
614614
@ html "<hr>\n"
615615
@ } else {
616616
@ html "<tr><td class='tktDspLabel' style='text-align:left'>\n"
617617
@ html "User Comments:</td></tr>\n"
618
-@ html "<tr><td colspan='5' class='tktDspValue'>\n"
618
+@ html "<tr><td colspan='5' class='tktDspValue'><div class='tktCommentArea'>\n"
619619
@ set seenRow 1
620620
@ }
621
+@ html "<div class='tktCommentEntry'>"
621622
@ html "<span class='tktDspCommenter'>"
622623
@ puts $xlogin
623624
@ if {$xlogin ne $xusername && [string length $xusername]>0} {
624625
@ puts " (claiming to be $xusername)"
625626
@ }
@@ -637,12 +638,13 @@
637638
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
638639
@ } else {
639640
@ set r [randhex]
640641
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
641642
@ }
643
+@ html "</div>"; # .tktCommentEntry
642644
@ }
643
-@ if {$seenRow} {html "</td></tr>\n"}
645
+@ if {$seenRow} {html "</div></td></tr>\n"}
644646
@ </th1>
645647
@ </table>
646648
;
647649
648650
@@ -789,11 +791,11 @@
789791
@ </tr>
790792
@
791793
@ <th1>
792794
@ set seenRow 0
793795
@ set alwaysPlaintext [info exists plaintext]
794
-@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
796
+@ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
795797
@ mimetype as xmimetype, icomment AS xcomment,
796798
@ username AS xusername
797799
@ FROM ticketchng
798800
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
799801
@ if {$seenRow} {
@@ -800,13 +802,14 @@
800802
@ html "<hr>\n"
801803
@ } else {
802804
@ html "<tr><td colspan='2'><hr></td></tr>\n"
803805
@ html "<tr><td colspan='2' class='tktDspLabel' style='text-align:left'>\n"
804806
@ html "Previous User Comments:</td></tr>\n"
805
-@ html "<tr><td colspan='2' class='tktDspValue'>\n"
807
+@ html "<tr><td colspan='2' class='tktDspValue'><div class='tktCommentArea'>\n"
806808
@ set seenRow 1
807809
@ }
810
+@ html "<div class='tktCommentEntry'>"
808811
@ html "<span class='tktDspCommenter'>"
809812
@ puts $xlogin
810813
@ if {$xlogin ne $xusername && [string length $xusername]>0} {
811814
@ puts " (claiming to be $xusername)"
812815
@ }
@@ -824,12 +827,13 @@
824827
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
825828
@ } else {
826829
@ set r [randhex]
827830
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
828831
@ }
832
+@ html "</div>"; # .tktCommentEntry
829833
@ }
830
-@ if {$seenRow} {html "</td></tr>\n"}
834
+@ if {$seenRow} {html "</div></td></tr>\n"}
831835
@ </th1>
832836
@
833837
@ </table>
834838
;
835839
@@ -926,12 +930,12 @@
926930
@ WHEN status='Fixed' THEN '#cfe8bd'
927931
@ WHEN status='Tested' THEN '#bde5d6'
928932
@ WHEN status='Deferred' THEN '#cacae5'
929933
@ ELSE '#c8c8c8' END AS 'bgcolor',
930934
@ substr(tkt_uuid,1,10) AS '#',
931
-@ datetime(tkt_ctime) AS 'created',
932
-@ datetime(tkt_mtime) AS 'modified',
935
+@ datetime(tkt_ctime,toLocal()) AS 'created',
936
+@ datetime(tkt_mtime,toLocal()) AS 'modified',
933937
@ type,
934938
@ status,
935939
@ subsystem,
936940
@ title,
937941
@ comment AS '_comments'
938942
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -603,23 +603,24 @@
603 @ }
604 @ }
605 @ }
606 @ set seenRow 0
607 @ set alwaysPlaintext [info exists plaintext]
608 @ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
609 @ mimetype as xmimetype, icomment AS xcomment,
610 @ username AS xusername
611 @ FROM ticketchng
612 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
613 @ if {$seenRow} {
614 @ html "<hr>\n"
615 @ } else {
616 @ html "<tr><td class='tktDspLabel' style='text-align:left'>\n"
617 @ html "User Comments:</td></tr>\n"
618 @ html "<tr><td colspan='5' class='tktDspValue'>\n"
619 @ set seenRow 1
620 @ }
 
621 @ html "<span class='tktDspCommenter'>"
622 @ puts $xlogin
623 @ if {$xlogin ne $xusername && [string length $xusername]>0} {
624 @ puts " (claiming to be $xusername)"
625 @ }
@@ -637,12 +638,13 @@
637 @ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
638 @ } else {
639 @ set r [randhex]
640 @ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
641 @ }
 
642 @ }
643 @ if {$seenRow} {html "</td></tr>\n"}
644 @ </th1>
645 @ </table>
646 ;
647
648
@@ -789,11 +791,11 @@
789 @ </tr>
790 @
791 @ <th1>
792 @ set seenRow 0
793 @ set alwaysPlaintext [info exists plaintext]
794 @ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
795 @ mimetype as xmimetype, icomment AS xcomment,
796 @ username AS xusername
797 @ FROM ticketchng
798 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
799 @ if {$seenRow} {
@@ -800,13 +802,14 @@
800 @ html "<hr>\n"
801 @ } else {
802 @ html "<tr><td colspan='2'><hr></td></tr>\n"
803 @ html "<tr><td colspan='2' class='tktDspLabel' style='text-align:left'>\n"
804 @ html "Previous User Comments:</td></tr>\n"
805 @ html "<tr><td colspan='2' class='tktDspValue'>\n"
806 @ set seenRow 1
807 @ }
 
808 @ html "<span class='tktDspCommenter'>"
809 @ puts $xlogin
810 @ if {$xlogin ne $xusername && [string length $xusername]>0} {
811 @ puts " (claiming to be $xusername)"
812 @ }
@@ -824,12 +827,13 @@
824 @ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
825 @ } else {
826 @ set r [randhex]
827 @ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
828 @ }
 
829 @ }
830 @ if {$seenRow} {html "</td></tr>\n"}
831 @ </th1>
832 @
833 @ </table>
834 ;
835
@@ -926,12 +930,12 @@
926 @ WHEN status='Fixed' THEN '#cfe8bd'
927 @ WHEN status='Tested' THEN '#bde5d6'
928 @ WHEN status='Deferred' THEN '#cacae5'
929 @ ELSE '#c8c8c8' END AS 'bgcolor',
930 @ substr(tkt_uuid,1,10) AS '#',
931 @ datetime(tkt_ctime) AS 'created',
932 @ datetime(tkt_mtime) AS 'modified',
933 @ type,
934 @ status,
935 @ subsystem,
936 @ title,
937 @ comment AS '_comments'
938
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -603,23 +603,24 @@
603 @ }
604 @ }
605 @ }
606 @ set seenRow 0
607 @ set alwaysPlaintext [info exists plaintext]
608 @ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
609 @ mimetype as xmimetype, icomment AS xcomment,
610 @ username AS xusername
611 @ FROM ticketchng
612 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
613 @ if {$seenRow} {
614 @ html "<hr>\n"
615 @ } else {
616 @ html "<tr><td class='tktDspLabel' style='text-align:left'>\n"
617 @ html "User Comments:</td></tr>\n"
618 @ html "<tr><td colspan='5' class='tktDspValue'><div class='tktCommentArea'>\n"
619 @ set seenRow 1
620 @ }
621 @ html "<div class='tktCommentEntry'>"
622 @ html "<span class='tktDspCommenter'>"
623 @ puts $xlogin
624 @ if {$xlogin ne $xusername && [string length $xusername]>0} {
625 @ puts " (claiming to be $xusername)"
626 @ }
@@ -637,12 +638,13 @@
638 @ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
639 @ } else {
640 @ set r [randhex]
641 @ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
642 @ }
643 @ html "</div>"; # .tktCommentEntry
644 @ }
645 @ if {$seenRow} {html "</div></td></tr>\n"}
646 @ </th1>
647 @ </table>
648 ;
649
650
@@ -789,11 +791,11 @@
791 @ </tr>
792 @
793 @ <th1>
794 @ set seenRow 0
795 @ set alwaysPlaintext [info exists plaintext]
796 @ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
797 @ mimetype as xmimetype, icomment AS xcomment,
798 @ username AS xusername
799 @ FROM ticketchng
800 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
801 @ if {$seenRow} {
@@ -800,13 +802,14 @@
802 @ html "<hr>\n"
803 @ } else {
804 @ html "<tr><td colspan='2'><hr></td></tr>\n"
805 @ html "<tr><td colspan='2' class='tktDspLabel' style='text-align:left'>\n"
806 @ html "Previous User Comments:</td></tr>\n"
807 @ html "<tr><td colspan='2' class='tktDspValue'><div class='tktCommentArea'>\n"
808 @ set seenRow 1
809 @ }
810 @ html "<div class='tktCommentEntry'>"
811 @ html "<span class='tktDspCommenter'>"
812 @ puts $xlogin
813 @ if {$xlogin ne $xusername && [string length $xusername]>0} {
814 @ puts " (claiming to be $xusername)"
815 @ }
@@ -824,12 +827,13 @@
827 @ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
828 @ } else {
829 @ set r [randhex]
830 @ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
831 @ }
832 @ html "</div>"; # .tktCommentEntry
833 @ }
834 @ if {$seenRow} {html "</div></td></tr>\n"}
835 @ </th1>
836 @
837 @ </table>
838 ;
839
@@ -926,12 +930,12 @@
930 @ WHEN status='Fixed' THEN '#cfe8bd'
931 @ WHEN status='Tested' THEN '#bde5d6'
932 @ WHEN status='Deferred' THEN '#cacae5'
933 @ ELSE '#c8c8c8' END AS 'bgcolor',
934 @ substr(tkt_uuid,1,10) AS '#',
935 @ datetime(tkt_ctime,toLocal()) AS 'created',
936 @ datetime(tkt_mtime,toLocal()) AS 'modified',
937 @ type,
938 @ status,
939 @ subsystem,
940 @ title,
941 @ comment AS '_comments'
942
--- src/unversioned.c
+++ src/unversioned.c
@@ -320,10 +320,13 @@
320320
const char *zError = 0;
321321
const char *zIn;
322322
const char *zAs;
323323
Blob file;
324324
int i;
325
+ i64 mxSize = sqlite3_limit(g.db,SQLITE_LIMIT_LENGTH,-1) - 850;
326
+ /* Extra space for other fields ------^^^ */
327
+ /* of the UNVESIONED table row. */
325328
326329
zAs = find_option("as",0,1);
327330
verify_all_options();
328331
if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
329332
db_begin_transaction();
@@ -336,13 +339,19 @@
336339
zError = "be absolute";
337340
}else if ( !file_is_simple_pathname(zIn,1) ){
338341
zError = "contain complex paths";
339342
}else if( contains_whitespace(zIn) ){
340343
zError = "contain whitespace";
344
+ }else if( strlen(zIn)>500 ){
345
+ zError = "be more than 500 bytes long";
341346
}
342347
if( zError ){
343348
fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
349
+ }
350
+ if( file_size(g.argv[i], ExtFILE)>mxSize ){
351
+ fossil_fatal("file \"%s\" is too big; max size: %,lld bytes",
352
+ g.argv[i], mxSize);
344353
}
345354
blob_init(&file,0,0);
346355
blob_read_from_file(&file, g.argv[i], ExtFILE);
347356
unversioned_write(zIn, &file, mtime);
348357
blob_reset(&file);
349358
--- src/unversioned.c
+++ src/unversioned.c
@@ -320,10 +320,13 @@
320 const char *zError = 0;
321 const char *zIn;
322 const char *zAs;
323 Blob file;
324 int i;
 
 
 
325
326 zAs = find_option("as",0,1);
327 verify_all_options();
328 if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
329 db_begin_transaction();
@@ -336,13 +339,19 @@
336 zError = "be absolute";
337 }else if ( !file_is_simple_pathname(zIn,1) ){
338 zError = "contain complex paths";
339 }else if( contains_whitespace(zIn) ){
340 zError = "contain whitespace";
 
 
341 }
342 if( zError ){
343 fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
 
 
 
 
344 }
345 blob_init(&file,0,0);
346 blob_read_from_file(&file, g.argv[i], ExtFILE);
347 unversioned_write(zIn, &file, mtime);
348 blob_reset(&file);
349
--- src/unversioned.c
+++ src/unversioned.c
@@ -320,10 +320,13 @@
320 const char *zError = 0;
321 const char *zIn;
322 const char *zAs;
323 Blob file;
324 int i;
325 i64 mxSize = sqlite3_limit(g.db,SQLITE_LIMIT_LENGTH,-1) - 850;
326 /* Extra space for other fields ------^^^ */
327 /* of the UNVESIONED table row. */
328
329 zAs = find_option("as",0,1);
330 verify_all_options();
331 if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
332 db_begin_transaction();
@@ -336,13 +339,19 @@
339 zError = "be absolute";
340 }else if ( !file_is_simple_pathname(zIn,1) ){
341 zError = "contain complex paths";
342 }else if( contains_whitespace(zIn) ){
343 zError = "contain whitespace";
344 }else if( strlen(zIn)>500 ){
345 zError = "be more than 500 bytes long";
346 }
347 if( zError ){
348 fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
349 }
350 if( file_size(g.argv[i], ExtFILE)>mxSize ){
351 fossil_fatal("file \"%s\" is too big; max size: %,lld bytes",
352 g.argv[i], mxSize);
353 }
354 blob_init(&file,0,0);
355 blob_read_from_file(&file, g.argv[i], ExtFILE);
356 unversioned_write(zIn, &file, mtime);
357 blob_reset(&file);
358
+3
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
5858
char *user; /* User id for http: */
5959
char *passwd; /* Password for http: */
6060
char *canonical; /* Canonical representation of the URL */
6161
char *proxyAuth; /* Proxy-Authorizer: string */
6262
char *fossil; /* The fossil query parameter on ssh: */
63
+ char *subpath; /* Secondary HTTP request path for ssh: and file: */
6364
char *pwConfig; /* CONFIG table entry that gave us the password */
6465
unsigned flags; /* Boolean flags controlling URL processing */
6566
int useProxy; /* Used to remember that a proxy is in use */
6667
int proxyOrigPort; /* Tunneled port number for https through proxy */
6768
char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
406407
fossil_free(p->name);
407408
fossil_free(p->path);
408409
fossil_free(p->user);
409410
fossil_free(p->passwd);
410411
fossil_free(p->fossil);
412
+ fossil_free(p->subpath);
411413
fossil_free(p->pwConfig);
412414
memset(p, 0, sizeof(*p));
413415
}
414416
415417
/*
@@ -483,10 +485,11 @@
483485
fossil_print("g.url.passwd = ************\n");
484486
}
485487
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
486488
fossil_print("g.url.canonical = %s\n", g.url.canonical);
487489
fossil_print("g.url.fossil = %s\n", g.url.fossil);
490
+ fossil_print("g.url.subpath = %s\n", g.url.subpath);
488491
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
489492
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
490493
}
491494
492495
/*
493496
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
58 char *user; /* User id for http: */
59 char *passwd; /* Password for http: */
60 char *canonical; /* Canonical representation of the URL */
61 char *proxyAuth; /* Proxy-Authorizer: string */
62 char *fossil; /* The fossil query parameter on ssh: */
 
63 char *pwConfig; /* CONFIG table entry that gave us the password */
64 unsigned flags; /* Boolean flags controlling URL processing */
65 int useProxy; /* Used to remember that a proxy is in use */
66 int proxyOrigPort; /* Tunneled port number for https through proxy */
67 char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
406 fossil_free(p->name);
407 fossil_free(p->path);
408 fossil_free(p->user);
409 fossil_free(p->passwd);
410 fossil_free(p->fossil);
 
411 fossil_free(p->pwConfig);
412 memset(p, 0, sizeof(*p));
413 }
414
415 /*
@@ -483,10 +485,11 @@
483 fossil_print("g.url.passwd = ************\n");
484 }
485 fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
486 fossil_print("g.url.canonical = %s\n", g.url.canonical);
487 fossil_print("g.url.fossil = %s\n", g.url.fossil);
 
488 fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
489 fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
490 }
491
492 /*
493
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
58 char *user; /* User id for http: */
59 char *passwd; /* Password for http: */
60 char *canonical; /* Canonical representation of the URL */
61 char *proxyAuth; /* Proxy-Authorizer: string */
62 char *fossil; /* The fossil query parameter on ssh: */
63 char *subpath; /* Secondary HTTP request path for ssh: and file: */
64 char *pwConfig; /* CONFIG table entry that gave us the password */
65 unsigned flags; /* Boolean flags controlling URL processing */
66 int useProxy; /* Used to remember that a proxy is in use */
67 int proxyOrigPort; /* Tunneled port number for https through proxy */
68 char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
407 fossil_free(p->name);
408 fossil_free(p->path);
409 fossil_free(p->user);
410 fossil_free(p->passwd);
411 fossil_free(p->fossil);
412 fossil_free(p->subpath);
413 fossil_free(p->pwConfig);
414 memset(p, 0, sizeof(*p));
415 }
416
417 /*
@@ -483,10 +485,11 @@
485 fossil_print("g.url.passwd = ************\n");
486 }
487 fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
488 fossil_print("g.url.canonical = %s\n", g.url.canonical);
489 fossil_print("g.url.fossil = %s\n", g.url.fossil);
490 fossil_print("g.url.subpath = %s\n", g.url.subpath);
491 fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
492 fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
493 }
494
495 /*
496
+4 -3
--- src/user.c
+++ src/user.c
@@ -109,10 +109,12 @@
109109
}
110110
void freepass(){
111111
if( !zPwdBuffer ) return;
112112
assert( nPwdBuffer>0 );
113113
fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
114
+ zPwdBuffer = 0;
115
+ nPwdBuffer = 0;
114116
}
115117
#endif
116118
117119
/*
118120
** Scramble substitution matrix:
@@ -286,13 +288,12 @@
286288
char *zPrompt = mprintf("\rpassword for %s: ", zUser);
287289
char *zPw;
288290
Blob x;
289291
fossil_force_newline();
290292
prompt_for_password(zPrompt, &x, 0);
291
- free(zPrompt);
292
- zPw = mprintf("%b", &x);
293
- blob_reset(&x);
293
+ fossil_free(zPrompt);
294
+ zPw = blob_str(&x)/*transfer ownership*/;
294295
return zPw;
295296
}
296297
297298
/*
298299
** Prompt the user to enter a single line of text.
299300
--- src/user.c
+++ src/user.c
@@ -109,10 +109,12 @@
109 }
110 void freepass(){
111 if( !zPwdBuffer ) return;
112 assert( nPwdBuffer>0 );
113 fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
 
 
114 }
115 #endif
116
117 /*
118 ** Scramble substitution matrix:
@@ -286,13 +288,12 @@
286 char *zPrompt = mprintf("\rpassword for %s: ", zUser);
287 char *zPw;
288 Blob x;
289 fossil_force_newline();
290 prompt_for_password(zPrompt, &x, 0);
291 free(zPrompt);
292 zPw = mprintf("%b", &x);
293 blob_reset(&x);
294 return zPw;
295 }
296
297 /*
298 ** Prompt the user to enter a single line of text.
299
--- src/user.c
+++ src/user.c
@@ -109,10 +109,12 @@
109 }
110 void freepass(){
111 if( !zPwdBuffer ) return;
112 assert( nPwdBuffer>0 );
113 fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
114 zPwdBuffer = 0;
115 nPwdBuffer = 0;
116 }
117 #endif
118
119 /*
120 ** Scramble substitution matrix:
@@ -286,13 +288,12 @@
288 char *zPrompt = mprintf("\rpassword for %s: ", zUser);
289 char *zPw;
290 Blob x;
291 fossil_force_newline();
292 prompt_for_password(zPrompt, &x, 0);
293 fossil_free(zPrompt);
294 zPw = blob_str(&x)/*transfer ownership*/;
 
295 return zPw;
296 }
297
298 /*
299 ** Prompt the user to enter a single line of text.
300
+42
--- src/util.c
+++ src/util.c
@@ -899,10 +899,52 @@
899899
}else{
900900
fossil_print("%s\n", zPassword);
901901
}
902902
fossil_free(zPassword);
903903
}
904
+
905
+/*
906
+** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4).
907
+**
908
+** Format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
909
+** where M=4 and N=8, 9, a, or b (this leaves 122 random bits)
910
+*/
911
+char* fossil_generate_uuid() {
912
+ static const char zDigits[] = "0123456789abcdef";
913
+ unsigned char aBlob[16];
914
+ unsigned char zStr[37];
915
+ unsigned char *p = zStr;
916
+ int i, k;
917
+
918
+ sqlite3_randomness(16, aBlob);
919
+ aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */
920
+ aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */
921
+
922
+ for(i=0, k=0x550; i<16; i++, k=k>>1){
923
+ if( k&1 ){
924
+ *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */
925
+ }
926
+ *p++ = zDigits[aBlob[i]>>4];
927
+ *p++ = zDigits[aBlob[i]&0xf];
928
+ }
929
+ *p = 0;
930
+
931
+ return fossil_strdup((char*)zStr);
932
+}
933
+
934
+/*
935
+** COMMAND: test-generate-uuid
936
+**
937
+** Usage: %fossil test-generate-uuid
938
+**
939
+** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4):
940
+**
941
+** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - where M=4 and N=8, 9, a, or b
942
+*/
943
+void test_generate_uuid(void){
944
+ fossil_print("%s\n", fossil_generate_uuid());
945
+}
904946
905947
/*
906948
** Return the number of decimal digits in a nonnegative integer. This is useful
907949
** when formatting text.
908950
*/
909951
--- src/util.c
+++ src/util.c
@@ -899,10 +899,52 @@
899 }else{
900 fossil_print("%s\n", zPassword);
901 }
902 fossil_free(zPassword);
903 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
905 /*
906 ** Return the number of decimal digits in a nonnegative integer. This is useful
907 ** when formatting text.
908 */
909
--- src/util.c
+++ src/util.c
@@ -899,10 +899,52 @@
899 }else{
900 fossil_print("%s\n", zPassword);
901 }
902 fossil_free(zPassword);
903 }
904
905 /*
906 ** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4).
907 **
908 ** Format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
909 ** where M=4 and N=8, 9, a, or b (this leaves 122 random bits)
910 */
911 char* fossil_generate_uuid() {
912 static const char zDigits[] = "0123456789abcdef";
913 unsigned char aBlob[16];
914 unsigned char zStr[37];
915 unsigned char *p = zStr;
916 int i, k;
917
918 sqlite3_randomness(16, aBlob);
919 aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */
920 aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */
921
922 for(i=0, k=0x550; i<16; i++, k=k>>1){
923 if( k&1 ){
924 *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */
925 }
926 *p++ = zDigits[aBlob[i]>>4];
927 *p++ = zDigits[aBlob[i]&0xf];
928 }
929 *p = 0;
930
931 return fossil_strdup((char*)zStr);
932 }
933
934 /*
935 ** COMMAND: test-generate-uuid
936 **
937 ** Usage: %fossil test-generate-uuid
938 **
939 ** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4):
940 **
941 ** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - where M=4 and N=8, 9, a, or b
942 */
943 void test_generate_uuid(void){
944 fossil_print("%s\n", fossil_generate_uuid());
945 }
946
947 /*
948 ** Return the number of decimal digits in a nonnegative integer. This is useful
949 ** when formatting text.
950 */
951
-1
--- src/xfer.c
+++ src/xfer.c
@@ -1333,11 +1333,10 @@
13331333
pzUuidList = &zUuidList;
13341334
pnUuidList = &nUuidList;
13351335
}
13361336
if( g.syncInfo.zLoginCard ){
13371337
/* Login card received via HTTP Cookie header */
1338
- assert( g.syncInfo.fLoginCardMode && "Set via HTTP cookie" );
13391338
blob_zero(&xfer.line);
13401339
blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
13411340
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
13421341
count(xfer.aToken));
13431342
fossil_free( g.syncInfo.zLoginCard );
13441343
--- src/xfer.c
+++ src/xfer.c
@@ -1333,11 +1333,10 @@
1333 pzUuidList = &zUuidList;
1334 pnUuidList = &nUuidList;
1335 }
1336 if( g.syncInfo.zLoginCard ){
1337 /* Login card received via HTTP Cookie header */
1338 assert( g.syncInfo.fLoginCardMode && "Set via HTTP cookie" );
1339 blob_zero(&xfer.line);
1340 blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
1341 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
1342 count(xfer.aToken));
1343 fossil_free( g.syncInfo.zLoginCard );
1344
--- src/xfer.c
+++ src/xfer.c
@@ -1333,11 +1333,10 @@
1333 pzUuidList = &zUuidList;
1334 pnUuidList = &nUuidList;
1335 }
1336 if( g.syncInfo.zLoginCard ){
1337 /* Login card received via HTTP Cookie header */
 
1338 blob_zero(&xfer.line);
1339 blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
1340 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
1341 count(xfer.aToken));
1342 fossil_free( g.syncInfo.zLoginCard );
1343
+24 -18
--- src/zip.c
+++ src/zip.c
@@ -406,16 +406,16 @@
406406
sqlite3_exec(p->db,
407407
"PRAGMA page_size=512;"
408408
"PRAGMA journal_mode = off;"
409409
"PRAGMA cache_spill = off;"
410410
"BEGIN;"
411
- "CREATE TABLE sqlar("
412
- "name TEXT PRIMARY KEY, -- name of the file\n"
413
- "mode INT, -- access permissions\n"
414
- "mtime INT, -- last modification time\n"
415
- "sz INT, -- original file size\n"
416
- "data BLOB -- compressed content\n"
411
+ "CREATE TABLE sqlar(\n"
412
+ " name TEXT PRIMARY KEY, -- name of the file\n"
413
+ " mode INT, -- access permissions\n"
414
+ " mtime INT, -- last modification time\n"
415
+ " sz INT, -- original file size\n"
416
+ " data BLOB -- compressed content\n"
417417
");", 0, 0, 0
418418
);
419419
sqlite3_prepare(p->db,
420420
"INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
421421
&p->pInsert, 0
@@ -864,19 +864,11 @@
864864
if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){
865865
zOut = 0;
866866
}
867867
868868
if( zName==0 ){
869
- zName = db_text("default-name",
870
- "SELECT replace(%Q,' ','_') "
871
- " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
872
- " || substr(blob.uuid, 1, 10)"
873
- " FROM event, blob"
874
- " WHERE event.objid=%d"
875
- " AND blob.rid=%d",
876
- db_get("project-name", "unnamed"), rid, rid
877
- );
869
+ zName = archive_base_name(rid);
878870
}
879871
zip_of_checkin(eType, rid, zOut ? &zip : 0,
880872
zName, pInclude, pExclude, listFlag);
881873
glob_free(pInclude);
882874
glob_free(pExclude);
@@ -995,10 +987,25 @@
995987
**
996988
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
997989
** comma-separated list of GLOB patterns, where each
998990
** pattern can optionally be quoted using ".." or '..'.
999991
** Any file matching both ex= and in= is excluded.
992
+**
993
+** Robot Defenses:
994
+**
995
+** * If "zip" appears in the robot-restrict setting, then robots are
996
+** not allowed to access this page. Suspected robots will be
997
+** presented with a captcha.
998
+**
999
+** * If "zipX" appears in the robot-restrict setting, then robots are
1000
+** restricted in the same way as with "zip", but with exceptions.
1001
+** If the check-in for which an archive is requested is a leaf check-in
1002
+** and if the robot-zip-leaf setting is true, then the request is
1003
+** allowed. Or if the check-in has a tag that matches any of the
1004
+** GLOB patterns on the list in the robot-zip-tag setting, then the
1005
+** request is allowed. Otherwise, the usual robot defenses are
1006
+** activated.
10001007
*/
10011008
void baseline_zip_page(void){
10021009
int rid;
10031010
const char *z;
10041011
char *zName, *zRid, *zKey;
@@ -1012,16 +1019,14 @@
10121019
int eType = ARCHIVE_ZIP; /* Type of archive to generate */
10131020
char *zType; /* Human-readable archive type */
10141021
10151022
login_check_credentials();
10161023
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1024
+ if( robot_restrict("zip") ) return;
10171025
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
10181026
eType = ARCHIVE_SQLAR;
10191027
zType = "SQL";
1020
- /* For some reason, SQL-archives are like catnip for robots. So
1021
- ** don't allow them to be downloaded by user "nobody" */
1022
- if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
10231028
}else{
10241029
eType = ARCHIVE_ZIP;
10251030
zType = "ZIP";
10261031
}
10271032
fossil_nice_default();
@@ -1068,10 +1073,11 @@
10681073
if( rid<=0 ){
10691074
cgi_set_status(404, "Not Found");
10701075
@ Not found
10711076
return;
10721077
}
1078
+ if( robot_restrict_zip(rid) ) return;
10731079
if( nRid==0 && nName>10 ) zName[10] = 0;
10741080
10751081
/* Compute a unique key for the cache entry based on query parameters */
10761082
blob_init(&cacheKey, 0, 0);
10771083
blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
10781084
--- src/zip.c
+++ src/zip.c
@@ -406,16 +406,16 @@
406 sqlite3_exec(p->db,
407 "PRAGMA page_size=512;"
408 "PRAGMA journal_mode = off;"
409 "PRAGMA cache_spill = off;"
410 "BEGIN;"
411 "CREATE TABLE sqlar("
412 "name TEXT PRIMARY KEY, -- name of the file\n"
413 "mode INT, -- access permissions\n"
414 "mtime INT, -- last modification time\n"
415 "sz INT, -- original file size\n"
416 "data BLOB -- compressed content\n"
417 ");", 0, 0, 0
418 );
419 sqlite3_prepare(p->db,
420 "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
421 &p->pInsert, 0
@@ -864,19 +864,11 @@
864 if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){
865 zOut = 0;
866 }
867
868 if( zName==0 ){
869 zName = db_text("default-name",
870 "SELECT replace(%Q,' ','_') "
871 " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
872 " || substr(blob.uuid, 1, 10)"
873 " FROM event, blob"
874 " WHERE event.objid=%d"
875 " AND blob.rid=%d",
876 db_get("project-name", "unnamed"), rid, rid
877 );
878 }
879 zip_of_checkin(eType, rid, zOut ? &zip : 0,
880 zName, pInclude, pExclude, listFlag);
881 glob_free(pInclude);
882 glob_free(pExclude);
@@ -995,10 +987,25 @@
995 **
996 ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
997 ** comma-separated list of GLOB patterns, where each
998 ** pattern can optionally be quoted using ".." or '..'.
999 ** Any file matching both ex= and in= is excluded.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1000 */
1001 void baseline_zip_page(void){
1002 int rid;
1003 const char *z;
1004 char *zName, *zRid, *zKey;
@@ -1012,16 +1019,14 @@
1012 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
1013 char *zType; /* Human-readable archive type */
1014
1015 login_check_credentials();
1016 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
 
1017 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1018 eType = ARCHIVE_SQLAR;
1019 zType = "SQL";
1020 /* For some reason, SQL-archives are like catnip for robots. So
1021 ** don't allow them to be downloaded by user "nobody" */
1022 if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
1023 }else{
1024 eType = ARCHIVE_ZIP;
1025 zType = "ZIP";
1026 }
1027 fossil_nice_default();
@@ -1068,10 +1073,11 @@
1068 if( rid<=0 ){
1069 cgi_set_status(404, "Not Found");
1070 @ Not found
1071 return;
1072 }
 
1073 if( nRid==0 && nName>10 ) zName[10] = 0;
1074
1075 /* Compute a unique key for the cache entry based on query parameters */
1076 blob_init(&cacheKey, 0, 0);
1077 blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
1078
--- src/zip.c
+++ src/zip.c
@@ -406,16 +406,16 @@
406 sqlite3_exec(p->db,
407 "PRAGMA page_size=512;"
408 "PRAGMA journal_mode = off;"
409 "PRAGMA cache_spill = off;"
410 "BEGIN;"
411 "CREATE TABLE sqlar(\n"
412 " name TEXT PRIMARY KEY, -- name of the file\n"
413 " mode INT, -- access permissions\n"
414 " mtime INT, -- last modification time\n"
415 " sz INT, -- original file size\n"
416 " data BLOB -- compressed content\n"
417 ");", 0, 0, 0
418 );
419 sqlite3_prepare(p->db,
420 "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
421 &p->pInsert, 0
@@ -864,19 +864,11 @@
864 if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){
865 zOut = 0;
866 }
867
868 if( zName==0 ){
869 zName = archive_base_name(rid);
 
 
 
 
 
 
 
 
870 }
871 zip_of_checkin(eType, rid, zOut ? &zip : 0,
872 zName, pInclude, pExclude, listFlag);
873 glob_free(pInclude);
874 glob_free(pExclude);
@@ -995,10 +987,25 @@
987 **
988 ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
989 ** comma-separated list of GLOB patterns, where each
990 ** pattern can optionally be quoted using ".." or '..'.
991 ** Any file matching both ex= and in= is excluded.
992 **
993 ** Robot Defenses:
994 **
995 ** * If "zip" appears in the robot-restrict setting, then robots are
996 ** not allowed to access this page. Suspected robots will be
997 ** presented with a captcha.
998 **
999 ** * If "zipX" appears in the robot-restrict setting, then robots are
1000 ** restricted in the same way as with "zip", but with exceptions.
1001 ** If the check-in for which an archive is requested is a leaf check-in
1002 ** and if the robot-zip-leaf setting is true, then the request is
1003 ** allowed. Or if the check-in has a tag that matches any of the
1004 ** GLOB patterns on the list in the robot-zip-tag setting, then the
1005 ** request is allowed. Otherwise, the usual robot defenses are
1006 ** activated.
1007 */
1008 void baseline_zip_page(void){
1009 int rid;
1010 const char *z;
1011 char *zName, *zRid, *zKey;
@@ -1012,16 +1019,14 @@
1019 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
1020 char *zType; /* Human-readable archive type */
1021
1022 login_check_credentials();
1023 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1024 if( robot_restrict("zip") ) return;
1025 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1026 eType = ARCHIVE_SQLAR;
1027 zType = "SQL";
 
 
 
1028 }else{
1029 eType = ARCHIVE_ZIP;
1030 zType = "ZIP";
1031 }
1032 fossil_nice_default();
@@ -1068,10 +1073,11 @@
1073 if( rid<=0 ){
1074 cgi_set_status(404, "Not Found");
1075 @ Not found
1076 return;
1077 }
1078 if( robot_restrict_zip(rid) ) return;
1079 if( nRid==0 && nName>10 ) zName[10] = 0;
1080
1081 /* Compute a unique key for the cache entry based on query parameters */
1082 blob_init(&cacheKey, 0, 0);
1083 blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
1084
--- test/commit-warning.test
+++ test/commit-warning.test
@@ -160,146 +160,7 @@
160160
1}]]}
161161
162162
163163
###############################################################################
164164
165
-# TODO: Change to a collection of test-case crafted files
166
-# rather than depend on this list of files that will
167
-# be fragile as development progresses.
168
-#
169
-# Unless the real goal of this test is to document a collection
170
-# of source files that MUST NEVER BE TEXT.
171
-#
172
-test_block_in_checkout pre-commit-warnings-fossil-1 {
173
- fossil test-commit-warning --no-settings
174
-} {
175
- test pre-commit-warnings-fossil-1 {[normalize_result] eq \
176
- [subst -nocommands -novariables [string trim {
177
-1\tcompat/zlib/contrib/blast/test.pk\tbinary data
178
-1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
179
-1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
180
-1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
181
-1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
182
-1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
183
-1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
184
-1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
185
-1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
186
-1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
187
-1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
188
-1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
189
-1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
190
-1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
191
-1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
192
-1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
193
-1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings
194
-1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
195
-1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
196
-1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
197
-1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
198
-1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
199
-1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
200
-1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings
201
-1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters\tCR/LF line endings
202
-1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj\tCR/LF line endings
203
-1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters\tCR/LF line endings
204
-1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj\tCR/LF line endings
205
-1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters\tCR/LF line endings
206
-1\tcompat/zlib/contrib/vstudio/vc10/zlib.rc\tCR/LF line endings
207
-1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj\tCR/LF line endings
208
-1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters\tCR/LF line endings
209
-1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.def\tCR/LF line endings
210
-1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.sln\tCR/LF line endings
211
-1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj\tCR/LF line endings
212
-1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters\tCR/LF line endings
213
-1\tcompat/zlib/contrib/vstudio/vc11/miniunz.vcxproj\tCR/LF line endings
214
-1\tcompat/zlib/contrib/vstudio/vc11/minizip.vcxproj\tCR/LF line endings
215
-1\tcompat/zlib/contrib/vstudio/vc11/testzlib.vcxproj\tCR/LF line endings
216
-1\tcompat/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj\tCR/LF line endings
217
-1\tcompat/zlib/contrib/vstudio/vc11/zlib.rc\tCR/LF line endings
218
-1\tcompat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj\tCR/LF line endings
219
-1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.def\tCR/LF line endings
220
-1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.sln\tCR/LF line endings
221
-1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj\tCR/LF line endings
222
-1\tcompat/zlib/contrib/vstudio/vc12/zlibvc.def\tCR/LF line endings
223
-1\tcompat/zlib/contrib/vstudio/vc14/zlibvc.def\tCR/LF line endings
224
-1\tcompat/zlib/contrib/vstudio/vc9/miniunz.vcproj\tCR/LF line endings
225
-1\tcompat/zlib/contrib/vstudio/vc9/minizip.vcproj\tCR/LF line endings
226
-1\tcompat/zlib/contrib/vstudio/vc9/testzlib.vcproj\tCR/LF line endings
227
-1\tcompat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj\tCR/LF line endings
228
-1\tcompat/zlib/contrib/vstudio/vc9/zlib.rc\tCR/LF line endings
229
-1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings
230
-1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings
231
-1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings
232
-1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings
233
-1\tcompat/zlib/win32/zlib.def\tCR/LF line endings
234
-1\tcompat/zlib/zlib.3.pdf\tbinary data
235
-1\tcompat/zlib/zlib.map\tCR/LF line endings
236
-1\textsrc/pikchr.wasm\tbinary data
237
-1\tskins/blitz/arrow_project.png\tbinary data
238
-1\tskins/blitz/dir.png\tbinary data
239
-1\tskins/blitz/file.png\tbinary data
240
-1\tskins/blitz/fossil_100.png\tbinary data
241
-1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data
242
-1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data
243
-1\tskins/blitz/rss_20.png\tbinary data
244
-1\tsrc/alerts/bflat2.wav\tbinary data
245
-1\tsrc/alerts/bflat3.wav\tbinary data
246
-1\tsrc/alerts/bloop.wav\tbinary data
247
-1\tsrc/alerts/plunk.wav\tbinary data
248
-1\tsrc/sounds/0.wav\tbinary data
249
-1\tsrc/sounds/1.wav\tbinary data
250
-1\tsrc/sounds/2.wav\tbinary data
251
-1\tsrc/sounds/3.wav\tbinary data
252
-1\tsrc/sounds/4.wav\tbinary data
253
-1\tsrc/sounds/5.wav\tbinary data
254
-1\tsrc/sounds/6.wav\tbinary data
255
-1\tsrc/sounds/7.wav\tbinary data
256
-1\tsrc/sounds/8.wav\tbinary data
257
-1\tsrc/sounds/9.wav\tbinary data
258
-1\tsrc/sounds/a.wav\tbinary data
259
-1\tsrc/sounds/b.wav\tbinary data
260
-1\tsrc/sounds/c.wav\tbinary data
261
-1\tsrc/sounds/d.wav\tbinary data
262
-1\tsrc/sounds/e.wav\tbinary data
263
-1\tsrc/sounds/f.wav\tbinary data
264
-1\ttest/th1-docs-input.txt\tCR/LF line endings
265
-1\ttest/th1-hooks-input.txt\tCR/LF line endings
266
-1\ttest/utf16be.txt\tUnicode
267
-1\ttest/utf16le.txt\tUnicode
268
-1\twin/buildmsvc.bat\tCR/LF line endings
269
-1\twin/fossil.ico\tbinary data
270
-1\twin/fossil.rc\tinvalid UTF-8
271
-1\twww/apple-touch-icon.png\tbinary data
272
-1\twww/background.jpg\tbinary data
273
-1\twww/build-icons/linux.gif\tbinary data
274
-1\twww/build-icons/linux64.gif\tbinary data
275
-1\twww/build-icons/mac.gif\tbinary data
276
-1\twww/build-icons/openbsd.gif\tbinary data
277
-1\twww/build-icons/src.gif\tbinary data
278
-1\twww/build-icons/win32.gif\tbinary data
279
-1\twww/copyright-release.pdf\tbinary data
280
-1\twww/encode1.gif\tbinary data
281
-1\twww/encode2.gif\tbinary data
282
-1\twww/encode3.gif\tbinary data
283
-1\twww/encode4.gif\tbinary data
284
-1\twww/encode5.gif\tbinary data
285
-1\twww/encode6.gif\tbinary data
286
-1\twww/encode7.gif\tbinary data
287
-1\twww/encode8.gif\tbinary data
288
-1\twww/encode9.gif\tbinary data
289
-1\twww/fossil.gif\tbinary data
290
-1\twww/fossil2.gif\tbinary data
291
-1\twww/fossil3.gif\tbinary data
292
-1\twww/fossil_logo_small.gif\tbinary data
293
-1\twww/fossil_logo_small2.gif\tbinary data
294
-1\twww/fossil_logo_small3.gif\tbinary data
295
-1\twww/server/windows/cgi-bin-perm.png\tbinary data
296
-1\twww/server/windows/cgi-exec-perm.png\tbinary data
297
-1\twww/server/windows/cgi-install-iis.png\tbinary data
298
-1\twww/server/windows/cgi-script-map.png\tbinary data
299
-1\twww/xkcd-git.gif\tbinary data
300
-1}]]}
301
-}
302
-
303
-###############################################################################
304165
305166
test_cleanup
306167
--- test/commit-warning.test
+++ test/commit-warning.test
@@ -160,146 +160,7 @@
160 1}]]}
161
162
163 ###############################################################################
164
165 # TODO: Change to a collection of test-case crafted files
166 # rather than depend on this list of files that will
167 # be fragile as development progresses.
168 #
169 # Unless the real goal of this test is to document a collection
170 # of source files that MUST NEVER BE TEXT.
171 #
172 test_block_in_checkout pre-commit-warnings-fossil-1 {
173 fossil test-commit-warning --no-settings
174 } {
175 test pre-commit-warnings-fossil-1 {[normalize_result] eq \
176 [subst -nocommands -novariables [string trim {
177 1\tcompat/zlib/contrib/blast/test.pk\tbinary data
178 1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
179 1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
180 1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
181 1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
182 1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
183 1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
184 1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
185 1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
186 1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
187 1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
188 1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
189 1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
190 1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
191 1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
192 1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
193 1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings
194 1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
195 1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
196 1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
197 1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
198 1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
199 1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
200 1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings
201 1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters\tCR/LF line endings
202 1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj\tCR/LF line endings
203 1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters\tCR/LF line endings
204 1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj\tCR/LF line endings
205 1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters\tCR/LF line endings
206 1\tcompat/zlib/contrib/vstudio/vc10/zlib.rc\tCR/LF line endings
207 1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj\tCR/LF line endings
208 1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters\tCR/LF line endings
209 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.def\tCR/LF line endings
210 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.sln\tCR/LF line endings
211 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj\tCR/LF line endings
212 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters\tCR/LF line endings
213 1\tcompat/zlib/contrib/vstudio/vc11/miniunz.vcxproj\tCR/LF line endings
214 1\tcompat/zlib/contrib/vstudio/vc11/minizip.vcxproj\tCR/LF line endings
215 1\tcompat/zlib/contrib/vstudio/vc11/testzlib.vcxproj\tCR/LF line endings
216 1\tcompat/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj\tCR/LF line endings
217 1\tcompat/zlib/contrib/vstudio/vc11/zlib.rc\tCR/LF line endings
218 1\tcompat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj\tCR/LF line endings
219 1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.def\tCR/LF line endings
220 1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.sln\tCR/LF line endings
221 1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj\tCR/LF line endings
222 1\tcompat/zlib/contrib/vstudio/vc12/zlibvc.def\tCR/LF line endings
223 1\tcompat/zlib/contrib/vstudio/vc14/zlibvc.def\tCR/LF line endings
224 1\tcompat/zlib/contrib/vstudio/vc9/miniunz.vcproj\tCR/LF line endings
225 1\tcompat/zlib/contrib/vstudio/vc9/minizip.vcproj\tCR/LF line endings
226 1\tcompat/zlib/contrib/vstudio/vc9/testzlib.vcproj\tCR/LF line endings
227 1\tcompat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj\tCR/LF line endings
228 1\tcompat/zlib/contrib/vstudio/vc9/zlib.rc\tCR/LF line endings
229 1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings
230 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings
231 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings
232 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings
233 1\tcompat/zlib/win32/zlib.def\tCR/LF line endings
234 1\tcompat/zlib/zlib.3.pdf\tbinary data
235 1\tcompat/zlib/zlib.map\tCR/LF line endings
236 1\textsrc/pikchr.wasm\tbinary data
237 1\tskins/blitz/arrow_project.png\tbinary data
238 1\tskins/blitz/dir.png\tbinary data
239 1\tskins/blitz/file.png\tbinary data
240 1\tskins/blitz/fossil_100.png\tbinary data
241 1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data
242 1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data
243 1\tskins/blitz/rss_20.png\tbinary data
244 1\tsrc/alerts/bflat2.wav\tbinary data
245 1\tsrc/alerts/bflat3.wav\tbinary data
246 1\tsrc/alerts/bloop.wav\tbinary data
247 1\tsrc/alerts/plunk.wav\tbinary data
248 1\tsrc/sounds/0.wav\tbinary data
249 1\tsrc/sounds/1.wav\tbinary data
250 1\tsrc/sounds/2.wav\tbinary data
251 1\tsrc/sounds/3.wav\tbinary data
252 1\tsrc/sounds/4.wav\tbinary data
253 1\tsrc/sounds/5.wav\tbinary data
254 1\tsrc/sounds/6.wav\tbinary data
255 1\tsrc/sounds/7.wav\tbinary data
256 1\tsrc/sounds/8.wav\tbinary data
257 1\tsrc/sounds/9.wav\tbinary data
258 1\tsrc/sounds/a.wav\tbinary data
259 1\tsrc/sounds/b.wav\tbinary data
260 1\tsrc/sounds/c.wav\tbinary data
261 1\tsrc/sounds/d.wav\tbinary data
262 1\tsrc/sounds/e.wav\tbinary data
263 1\tsrc/sounds/f.wav\tbinary data
264 1\ttest/th1-docs-input.txt\tCR/LF line endings
265 1\ttest/th1-hooks-input.txt\tCR/LF line endings
266 1\ttest/utf16be.txt\tUnicode
267 1\ttest/utf16le.txt\tUnicode
268 1\twin/buildmsvc.bat\tCR/LF line endings
269 1\twin/fossil.ico\tbinary data
270 1\twin/fossil.rc\tinvalid UTF-8
271 1\twww/apple-touch-icon.png\tbinary data
272 1\twww/background.jpg\tbinary data
273 1\twww/build-icons/linux.gif\tbinary data
274 1\twww/build-icons/linux64.gif\tbinary data
275 1\twww/build-icons/mac.gif\tbinary data
276 1\twww/build-icons/openbsd.gif\tbinary data
277 1\twww/build-icons/src.gif\tbinary data
278 1\twww/build-icons/win32.gif\tbinary data
279 1\twww/copyright-release.pdf\tbinary data
280 1\twww/encode1.gif\tbinary data
281 1\twww/encode2.gif\tbinary data
282 1\twww/encode3.gif\tbinary data
283 1\twww/encode4.gif\tbinary data
284 1\twww/encode5.gif\tbinary data
285 1\twww/encode6.gif\tbinary data
286 1\twww/encode7.gif\tbinary data
287 1\twww/encode8.gif\tbinary data
288 1\twww/encode9.gif\tbinary data
289 1\twww/fossil.gif\tbinary data
290 1\twww/fossil2.gif\tbinary data
291 1\twww/fossil3.gif\tbinary data
292 1\twww/fossil_logo_small.gif\tbinary data
293 1\twww/fossil_logo_small2.gif\tbinary data
294 1\twww/fossil_logo_small3.gif\tbinary data
295 1\twww/server/windows/cgi-bin-perm.png\tbinary data
296 1\twww/server/windows/cgi-exec-perm.png\tbinary data
297 1\twww/server/windows/cgi-install-iis.png\tbinary data
298 1\twww/server/windows/cgi-script-map.png\tbinary data
299 1\twww/xkcd-git.gif\tbinary data
300 1}]]}
301 }
302
303 ###############################################################################
304
305 test_cleanup
306
--- test/commit-warning.test
+++ test/commit-warning.test
@@ -160,146 +160,7 @@
160 1}]]}
161
162
163 ###############################################################################
164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
166 test_cleanup
167
+1 -1
--- test/json.test
+++ test/json.test
@@ -176,11 +176,11 @@
176176
test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
177177
}
178178
179179
#### VERSION AKA HAI
180180
181
-# The JSON API generally assumes we have a respository, so let it have one.
181
+# The JSON API generally assumes we have a repository, so let it have one.
182182
183183
# Set FOSSIL_USER to ensure consistent results in "json user list"
184184
set _fossil_user ""
185185
if [info exists env(FOSSIL_USER)] {
186186
set _fossil_user $env(FOSSIL_USER)
187187
--- test/json.test
+++ test/json.test
@@ -176,11 +176,11 @@
176 test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
177 }
178
179 #### VERSION AKA HAI
180
181 # The JSON API generally assumes we have a respository, so let it have one.
182
183 # Set FOSSIL_USER to ensure consistent results in "json user list"
184 set _fossil_user ""
185 if [info exists env(FOSSIL_USER)] {
186 set _fossil_user $env(FOSSIL_USER)
187
--- test/json.test
+++ test/json.test
@@ -176,11 +176,11 @@
176 test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
177 }
178
179 #### VERSION AKA HAI
180
181 # The JSON API generally assumes we have a repository, so let it have one.
182
183 # Set FOSSIL_USER to ensure consistent results in "json user list"
184 set _fossil_user ""
185 if [info exists env(FOSSIL_USER)] {
186 set _fossil_user $env(FOSSIL_USER)
187
--- test/set-manifest.test
+++ test/set-manifest.test
@@ -38,11 +38,11 @@
3838
if {[catch {package require sha1}] != 0} {
3939
puts "The \"sha1\" package is not available."
4040
test_cleanup_then_return
4141
}
4242
43
-# We need a respository, so let it have one.
43
+# We need a repository, so let it have one.
4444
test_setup
4545
4646
#### Verify classic behavior of the manifest setting
4747
4848
# Setting is off by default, and there are no extra files.
4949
5050
DELETED test/settings.test
5151
ADDED test/settings.test.off
--- test/set-manifest.test
+++ test/set-manifest.test
@@ -38,11 +38,11 @@
38 if {[catch {package require sha1}] != 0} {
39 puts "The \"sha1\" package is not available."
40 test_cleanup_then_return
41 }
42
43 # We need a respository, so let it have one.
44 test_setup
45
46 #### Verify classic behavior of the manifest setting
47
48 # Setting is off by default, and there are no extra files.
49
50 ELETED test/settings.test
51 DDED test/settings.test.off
--- test/set-manifest.test
+++ test/set-manifest.test
@@ -38,11 +38,11 @@
38 if {[catch {package require sha1}] != 0} {
39 puts "The \"sha1\" package is not available."
40 test_cleanup_then_return
41 }
42
43 # We need a repository, so let it have one.
44 test_setup
45
46 #### Verify classic behavior of the manifest setting
47
48 # Setting is off by default, and there are no extra files.
49
50 ELETED test/settings.test
51 DDED test/settings.test.off
D test/settings.test
-138
--- a/test/settings.test
+++ b/test/settings.test
@@ -1,138 +0,0 @@
1
-#
2
-# Copyright (c) 2016 D. Richard Hipp
3
-#
4
-# This program is free software; you can redistribute it and/or
5
-# modify it under the terms of the Simplified BSD License (also
6
-# known as the "2-Clause License" or "FreeBSD License".)
7
-#
8
-# This program is distributed in the hope that it will be useful,
9
-# but without any warranty; without even the implied warranty of
10
-# merchantability or fitness for a particular purpose.
11
-#
12
-# Author contact information:
13
-# [email protected]
14
-# http://www.hwaci.com/drh/
15
-#
16
-############################################################################
17
-#
18
-# The "settings" and "unset" commands.
19
-#
20
-
21
-set path [file dirname [info script]]; test_setup
22
-
23
-###############################################################################
24
-#
25
-# Complete syntax as tested:
26
-#
27
-# fossil settings ?PROPERTY? ?VALUE? ?OPTIONS?
28
-# fossil unset PROPERTY ?OPTIONS?
29
-#
30
-# Where the only supported options are "--global" and "--exact".
31
-#
32
-###############################################################################
33
-#
34
-# NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list
35
-# of settings to test and needs to be manually updated when new settings
36
-# are added.
37
-#
38
-###############################################################################
39
-#
40
-# NOTE: The [extract_setting_names] procedure extracts the list of setting
41
-# names from the line-ending normalized output of the "fossil settings"
42
-# command. It assumes that a setting name must begin with a lowercase
43
-# letter. It also assumes that any output lines that start with a
44
-# lowercase letter contain a setting name starting at that same point.
45
-#
46
-proc extract_setting_names { data } {
47
- set names [list]
48
-
49
- foreach {dummy name} [regexp \
50
- -all -line -inline -- {^([a-z][a-z0-9\-]*) ?.*$} $data] {
51
- lappend names $name
52
- }
53
-
54
- return $names
55
-}
56
-
57
-###############################################################################
58
-
59
-set all_settings [get_all_settings]
60
-
61
-fossil settings
62
-set local_settings [extract_setting_names [normalize_result_no_trim]]
63
-
64
-fossil settings --global
65
-set global_settings [extract_setting_names [normalize_result_no_trim]]
66
-
67
-foreach name $all_settings {
68
- test settings-have-local-$name {
69
- [lsearch -exact $local_settings $name] != -1
70
- }
71
-
72
- test settings-have-global-$name {
73
- [lsearch -exact $global_settings $name] != -1
74
- }
75
-}
76
-
77
-foreach name $local_settings {
78
- test settings-valid-local-$name {
79
- [lsearch -exact $all_settings $name] != -1
80
- }
81
-}
82
-
83
-foreach name $global_settings {
84
- test settings-valid-global-$name {
85
- [lsearch -exact $all_settings $name] != -1
86
- }
87
-}
88
-
89
-###############################################################################
90
-
91
-set pattern(1) {^%name%$}
92
-set pattern(2) {^%name%[ ]+\((?:local|global)\)[ ]+[^ ]+$}
93
-
94
-foreach name $all_settings {
95
- fossil settings $name --exact
96
- set data [normalize_result]
97
-
98
- test settings-query-local-$name {
99
- [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
100
- [regexp -- [string map [list %name% $name] $pattern(2)] $data]
101
- }
102
-
103
- if {$name eq "manifest"} {
104
- fossil settings $name --exact --global -expectError
105
- } else {
106
- fossil settings $name --exact --global
107
- }
108
- set data [normalize_result]
109
-
110
- if {$name eq "manifest"} {
111
- test settings-query-global-$name {
112
- $data eq "cannot set 'manifest' globally"
113
- }
114
- } else {
115
- test settings-query-global-$name {
116
- [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
117
- [regexp -- [string map [list %name% $name] $pattern(2)] $data]
118
- }
119
- }
120
-}
121
-
122
-###############################################################################
123
-
124
-fossil settings bad-setting -expectError
125
-
126
-test settings-query-bad-local {
127
- [normalize_result] eq "no such setting: bad-setting"
128
-}
129
-
130
-fossil settings bad-setting --global -expectError
131
-
132
-test settings-query-bad-global {
133
- [normalize_result] eq "no such setting: bad-setting"
134
-}
135
-
136
-###############################################################################
137
-
138
-test_cleanup
--- a/test/settings.test
+++ b/test/settings.test
@@ -1,138 +0,0 @@
1 #
2 # Copyright (c) 2016 D. Richard Hipp
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the Simplified BSD License (also
6 # known as the "2-Clause License" or "FreeBSD License".)
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but without any warranty; without even the implied warranty of
10 # merchantability or fitness for a particular purpose.
11 #
12 # Author contact information:
13 # [email protected]
14 # http://www.hwaci.com/drh/
15 #
16 ############################################################################
17 #
18 # The "settings" and "unset" commands.
19 #
20
21 set path [file dirname [info script]]; test_setup
22
23 ###############################################################################
24 #
25 # Complete syntax as tested:
26 #
27 # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS?
28 # fossil unset PROPERTY ?OPTIONS?
29 #
30 # Where the only supported options are "--global" and "--exact".
31 #
32 ###############################################################################
33 #
34 # NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list
35 # of settings to test and needs to be manually updated when new settings
36 # are added.
37 #
38 ###############################################################################
39 #
40 # NOTE: The [extract_setting_names] procedure extracts the list of setting
41 # names from the line-ending normalized output of the "fossil settings"
42 # command. It assumes that a setting name must begin with a lowercase
43 # letter. It also assumes that any output lines that start with a
44 # lowercase letter contain a setting name starting at that same point.
45 #
46 proc extract_setting_names { data } {
47 set names [list]
48
49 foreach {dummy name} [regexp \
50 -all -line -inline -- {^([a-z][a-z0-9\-]*) ?.*$} $data] {
51 lappend names $name
52 }
53
54 return $names
55 }
56
57 ###############################################################################
58
59 set all_settings [get_all_settings]
60
61 fossil settings
62 set local_settings [extract_setting_names [normalize_result_no_trim]]
63
64 fossil settings --global
65 set global_settings [extract_setting_names [normalize_result_no_trim]]
66
67 foreach name $all_settings {
68 test settings-have-local-$name {
69 [lsearch -exact $local_settings $name] != -1
70 }
71
72 test settings-have-global-$name {
73 [lsearch -exact $global_settings $name] != -1
74 }
75 }
76
77 foreach name $local_settings {
78 test settings-valid-local-$name {
79 [lsearch -exact $all_settings $name] != -1
80 }
81 }
82
83 foreach name $global_settings {
84 test settings-valid-global-$name {
85 [lsearch -exact $all_settings $name] != -1
86 }
87 }
88
89 ###############################################################################
90
91 set pattern(1) {^%name%$}
92 set pattern(2) {^%name%[ ]+\((?:local|global)\)[ ]+[^ ]+$}
93
94 foreach name $all_settings {
95 fossil settings $name --exact
96 set data [normalize_result]
97
98 test settings-query-local-$name {
99 [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
100 [regexp -- [string map [list %name% $name] $pattern(2)] $data]
101 }
102
103 if {$name eq "manifest"} {
104 fossil settings $name --exact --global -expectError
105 } else {
106 fossil settings $name --exact --global
107 }
108 set data [normalize_result]
109
110 if {$name eq "manifest"} {
111 test settings-query-global-$name {
112 $data eq "cannot set 'manifest' globally"
113 }
114 } else {
115 test settings-query-global-$name {
116 [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
117 [regexp -- [string map [list %name% $name] $pattern(2)] $data]
118 }
119 }
120 }
121
122 ###############################################################################
123
124 fossil settings bad-setting -expectError
125
126 test settings-query-bad-local {
127 [normalize_result] eq "no such setting: bad-setting"
128 }
129
130 fossil settings bad-setting --global -expectError
131
132 test settings-query-bad-global {
133 [normalize_result] eq "no such setting: bad-setting"
134 }
135
136 ###############################################################################
137
138 test_cleanup
--- a/test/settings.test
+++ b/test/settings.test
@@ -1,138 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

No diff available

--- a/test/settings.test.off
+++ b/test/settings.test.off
@@ -0,0 +1,138 @@
1
+#
2
+# Copyright (c) 2016 D. Richard Hipp
3
+#
4
+# This program is free software; you can redistribute it and/or
5
+# modify it under the terms of the Simplified BSD License (also
6
+# known as the "2-Clause License" or "FreeBSD License".)
7
+#
8
+# This program is distributed in the hope that it will be useful,
9
+# but without any warranty; without even the implied warranty of
10
+# merchantability or fitness for a particular purpose.
11
+#
12
+# Author contact information:
13
+# [email protected]
14
+# http://www.hwaci.com/drh/
15
+#
16
+############################################################################
17
+#
18
+# The "settings" and "unset" commands.
19
+#
20
+
21
+set path [file dirname [info script]]; test_setup
22
+
23
+###############################################################################
24
+#
25
+# Complete syntax as tested:
26
+#
27
+# fossil settings ?PROPERTY? ?VALUE? ?OPTIONS?
28
+# fossil unset PROPERTY ?OPTIONS?
29
+#
30
+# Where the only supported options are "--global" and "--exact".
31
+#
32
+###############################################################################
33
+#
34
+# NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list
35
+# of settings to test and needs to be manually updated when new settings
36
+# are added.
37
+#
38
+###############################################################################
39
+#
40
+# NOTE: The [extract_setting_names] procedure extracts the list of setting
41
+# names from the line-ending normalized output of the "fossil settings"
42
+# command. It assumes that a setting name must begin with a lowercase
43
+# letter. It also assumes that any output lines that start with a
44
+# lowercase letter contain a setting name starting at that same point.
45
+#
46
+proc extract_setting_names { data } {
47
+ set names [list]
48
+
49
+ foreach {dummy name} [regexp \
50
+ -all -line -inline -- {^([a-z][a-z0-9\-]*) ?.*$} $data] {
51
+ lappend names $name
52
+ }
53
+
54
+ return $names
55
+}
56
+
57
+###############################################################################
58
+
59
+set all_settings [get_all_settings]
60
+
61
+fossil settings
62
+set local_settings [extract_setting_names [normalize_result_no_trim]]
63
+
64
+fossil settings --global
65
+set global_settings [extract_setting_names [normalize_result_no_trim]]
66
+
67
+foreach name $all_settings {
68
+ test settings-have-local-$name {
69
+ [lsearch -exact $local_settings $name] != -1
70
+ }
71
+
72
+ test settings-have-global-$name {
73
+ [lsearch -exact $global_settings $name] != -1
74
+ }
75
+}
76
+
77
+foreach name $local_settings {
78
+ test settings-valid-local-$name {
79
+ [lsearch -exact $all_settings $name] != -1
80
+ }
81
+}
82
+
83
+foreach name $global_settings {
84
+ test settings-valid-global-$name {
85
+ [lsearch -exact $all_settings $name] != -1
86
+ }
87
+}
88
+
89
+###############################################################################
90
+
91
+set pattern(1) {^%name%$}
92
+set pattern(2) {^%name%[ ]+\((?:local|global)\)[ ]+[^ ]+$}
93
+
94
+foreach name $all_settings {
95
+ fossil settings $name --exact
96
+ set data [normalize_result]
97
+
98
+ test settings-query-local-$name {
99
+ [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
100
+ [regexp -- [string map [list %name% $name] $pattern(2)] $data]
101
+ }
102
+
103
+ if {$name eq "manifest"} {
104
+ fossil settings $name --exact --global -expectError
105
+ } else {
106
+ fossil settings $name --exact --global
107
+ }
108
+ set data [normalize_result]
109
+
110
+ if {$name eq "manifest"} {
111
+ test settings-query-global-$name {
112
+ $data eq "cannot set 'manifest' globally"
113
+ }
114
+ } else {
115
+ test settings-query-global-$name {
116
+ [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
117
+ [regexp -- [string map [list %name% $name] $pattern(2)] $data]
118
+ }
119
+ }
120
+}
121
+
122
+###############################################################################
123
+
124
+fossil settings bad-setting -expectError
125
+
126
+test settings-query-bad-local {
127
+ [normalize_result] eq "no such setting: bad-setting"
128
+}
129
+
130
+fossil settings bad-setting --global -expectError
131
+
132
+test settings-query-bad-global {
133
+ [normalize_result] eq "no such setting: bad-setting"
134
+}
135
+
136
+###############################################################################
137
+
138
+test_cleanup
--- a/test/settings.test.off
+++ b/test/settings.test.off
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/settings.test.off
+++ b/test/settings.test.off
@@ -0,0 +1,138 @@
1 #
2 # Copyright (c) 2016 D. Richard Hipp
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the Simplified BSD License (also
6 # known as the "2-Clause License" or "FreeBSD License".)
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but without any warranty; without even the implied warranty of
10 # merchantability or fitness for a particular purpose.
11 #
12 # Author contact information:
13 # [email protected]
14 # http://www.hwaci.com/drh/
15 #
16 ############################################################################
17 #
18 # The "settings" and "unset" commands.
19 #
20
21 set path [file dirname [info script]]; test_setup
22
23 ###############################################################################
24 #
25 # Complete syntax as tested:
26 #
27 # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS?
28 # fossil unset PROPERTY ?OPTIONS?
29 #
30 # Where the only supported options are "--global" and "--exact".
31 #
32 ###############################################################################
33 #
34 # NOTE: The [get_all_settings] procedure from test/tester.tcl returns the list
35 # of settings to test and needs to be manually updated when new settings
36 # are added.
37 #
38 ###############################################################################
39 #
40 # NOTE: The [extract_setting_names] procedure extracts the list of setting
41 # names from the line-ending normalized output of the "fossil settings"
42 # command. It assumes that a setting name must begin with a lowercase
43 # letter. It also assumes that any output lines that start with a
44 # lowercase letter contain a setting name starting at that same point.
45 #
46 proc extract_setting_names { data } {
47 set names [list]
48
49 foreach {dummy name} [regexp \
50 -all -line -inline -- {^([a-z][a-z0-9\-]*) ?.*$} $data] {
51 lappend names $name
52 }
53
54 return $names
55 }
56
57 ###############################################################################
58
59 set all_settings [get_all_settings]
60
61 fossil settings
62 set local_settings [extract_setting_names [normalize_result_no_trim]]
63
64 fossil settings --global
65 set global_settings [extract_setting_names [normalize_result_no_trim]]
66
67 foreach name $all_settings {
68 test settings-have-local-$name {
69 [lsearch -exact $local_settings $name] != -1
70 }
71
72 test settings-have-global-$name {
73 [lsearch -exact $global_settings $name] != -1
74 }
75 }
76
77 foreach name $local_settings {
78 test settings-valid-local-$name {
79 [lsearch -exact $all_settings $name] != -1
80 }
81 }
82
83 foreach name $global_settings {
84 test settings-valid-global-$name {
85 [lsearch -exact $all_settings $name] != -1
86 }
87 }
88
89 ###############################################################################
90
91 set pattern(1) {^%name%$}
92 set pattern(2) {^%name%[ ]+\((?:local|global)\)[ ]+[^ ]+$}
93
94 foreach name $all_settings {
95 fossil settings $name --exact
96 set data [normalize_result]
97
98 test settings-query-local-$name {
99 [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
100 [regexp -- [string map [list %name% $name] $pattern(2)] $data]
101 }
102
103 if {$name eq "manifest"} {
104 fossil settings $name --exact --global -expectError
105 } else {
106 fossil settings $name --exact --global
107 }
108 set data [normalize_result]
109
110 if {$name eq "manifest"} {
111 test settings-query-global-$name {
112 $data eq "cannot set 'manifest' globally"
113 }
114 } else {
115 test settings-query-global-$name {
116 [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
117 [regexp -- [string map [list %name% $name] $pattern(2)] $data]
118 }
119 }
120 }
121
122 ###############################################################################
123
124 fossil settings bad-setting -expectError
125
126 test settings-query-bad-local {
127 [normalize_result] eq "no such setting: bad-setting"
128 }
129
130 fossil settings bad-setting --global -expectError
131
132 test settings-query-bad-global {
133 [normalize_result] eq "no such setting: bad-setting"
134 }
135
136 ###############################################################################
137
138 test_cleanup
--- tools/makeheaders.c
+++ tools/makeheaders.c
@@ -486,18 +486,18 @@
486486
/*
487487
** Compute a hash on a string. The number returned is a non-negative
488488
** value between 0 and 2**31 - 1
489489
*/
490490
static int Hash(const char *z, int n){
491
- int h = 0;
491
+ unsigned int h = 0;
492492
if( n<=0 ){
493493
n = strlen(z);
494494
}
495495
while( n-- ){
496496
h = h ^ (h<<5) ^ *z++;
497497
}
498
- return h & 0x7fffffff;
498
+ return (int)(h & 0x7fffffff);
499499
}
500500
501501
/*
502502
** Given an identifier name, try to find a declaration for that
503503
** identifier in the hash table. If found, return a pointer to
504504
--- tools/makeheaders.c
+++ tools/makeheaders.c
@@ -486,18 +486,18 @@
486 /*
487 ** Compute a hash on a string. The number returned is a non-negative
488 ** value between 0 and 2**31 - 1
489 */
490 static int Hash(const char *z, int n){
491 int h = 0;
492 if( n<=0 ){
493 n = strlen(z);
494 }
495 while( n-- ){
496 h = h ^ (h<<5) ^ *z++;
497 }
498 return h & 0x7fffffff;
499 }
500
501 /*
502 ** Given an identifier name, try to find a declaration for that
503 ** identifier in the hash table. If found, return a pointer to
504
--- tools/makeheaders.c
+++ tools/makeheaders.c
@@ -486,18 +486,18 @@
486 /*
487 ** Compute a hash on a string. The number returned is a non-negative
488 ** value between 0 and 2**31 - 1
489 */
490 static int Hash(const char *z, int n){
491 unsigned int h = 0;
492 if( n<=0 ){
493 n = strlen(z);
494 }
495 while( n-- ){
496 h = h ^ (h<<5) ^ *z++;
497 }
498 return (int)(h & 0x7fffffff);
499 }
500
501 /*
502 ** Given an identifier name, try to find a declaration for that
503 ** identifier in the hash table. If found, return a pointer to
504
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -152,10 +152,11 @@
152152
purge
153153
rebuild
154154
regexp
155155
repolist
156156
report
157
+ robot
157158
rss
158159
schema
159160
search
160161
security_audit
161162
setup
162163
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -152,10 +152,11 @@
152 purge
153 rebuild
154 regexp
155 repolist
156 report
 
157 rss
158 schema
159 search
160 security_audit
161 setup
162
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -152,10 +152,11 @@
152 purge
153 rebuild
154 regexp
155 repolist
156 report
157 robot
158 rss
159 schema
160 search
161 security_audit
162 setup
163
+10 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
3232
3333
SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3434
3535
PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
3636
37
-SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
37
+SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c robot_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
3838
39
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
39
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\robot$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
4040
4141
4242
RC=$(DMDIR)\bin\rcc
4343
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4444
@@ -53,11 +53,11 @@
5353
5454
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5555
$(RC) $(RCFLAGS) -o$@ $**
5656
5757
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58
- +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
58
+ +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report robot rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
5959
+echo fossil >> $@
6060
+echo fossil >> $@
6161
+echo $(LIBS) >> $@
6262
+echo. >> $@
6363
+echo fossil >> $@
@@ -755,10 +755,16 @@
755755
$(OBJDIR)\report$O : report_.c report.h
756756
$(TCC) -o$@ -c report_.c
757757
758758
report_.c : $(SRCDIR)\report.c
759759
+translate$E $** > $@
760
+
761
+$(OBJDIR)\robot$O : robot_.c robot.h
762
+ $(TCC) -o$@ -c robot_.c
763
+
764
+robot_.c : $(SRCDIR)\robot.c
765
+ +translate$E $** > $@
760766
761767
$(OBJDIR)\rss$O : rss_.c rss.h
762768
$(TCC) -o$@ -c rss_.c
763769
764770
rss_.c : $(SRCDIR)\rss.c
@@ -1015,7 +1021,7 @@
10151021
10161022
zip_.c : $(SRCDIR)\zip.c
10171023
+translate$E $** > $@
10181024
10191025
headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1020
- +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1026
+ +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h robot_.c:robot.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
10211027
@copy /Y nul: headers
10221028
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
32
33 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
34
35 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
36
37 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
38
39 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
40
41
42 RC=$(DMDIR)\bin\rcc
43 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
44
@@ -53,11 +53,11 @@
53
54 $(OBJDIR)\fossil.res: $B\win\fossil.rc
55 $(RC) $(RCFLAGS) -o$@ $**
56
57 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
59 +echo fossil >> $@
60 +echo fossil >> $@
61 +echo $(LIBS) >> $@
62 +echo. >> $@
63 +echo fossil >> $@
@@ -755,10 +755,16 @@
755 $(OBJDIR)\report$O : report_.c report.h
756 $(TCC) -o$@ -c report_.c
757
758 report_.c : $(SRCDIR)\report.c
759 +translate$E $** > $@
 
 
 
 
 
 
760
761 $(OBJDIR)\rss$O : rss_.c rss.h
762 $(TCC) -o$@ -c rss_.c
763
764 rss_.c : $(SRCDIR)\rss.c
@@ -1015,7 +1021,7 @@
1015
1016 zip_.c : $(SRCDIR)\zip.c
1017 +translate$E $** > $@
1018
1019 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1020 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1021 @copy /Y nul: headers
1022
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
32
33 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
34
35 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
36
37 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c robot_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
38
39 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\robot$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
40
41
42 RC=$(DMDIR)\bin\rcc
43 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
44
@@ -53,11 +53,11 @@
53
54 $(OBJDIR)\fossil.res: $B\win\fossil.rc
55 $(RC) $(RCFLAGS) -o$@ $**
56
57 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report robot rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
59 +echo fossil >> $@
60 +echo fossil >> $@
61 +echo $(LIBS) >> $@
62 +echo. >> $@
63 +echo fossil >> $@
@@ -755,10 +755,16 @@
755 $(OBJDIR)\report$O : report_.c report.h
756 $(TCC) -o$@ -c report_.c
757
758 report_.c : $(SRCDIR)\report.c
759 +translate$E $** > $@
760
761 $(OBJDIR)\robot$O : robot_.c robot.h
762 $(TCC) -o$@ -c robot_.c
763
764 robot_.c : $(SRCDIR)\robot.c
765 +translate$E $** > $@
766
767 $(OBJDIR)\rss$O : rss_.c rss.h
768 $(TCC) -o$@ -c rss_.c
769
770 rss_.c : $(SRCDIR)\rss.c
@@ -1015,7 +1021,7 @@
1021
1022 zip_.c : $(SRCDIR)\zip.c
1023 +translate$E $** > $@
1024
1025 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1026 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h robot_.c:robot.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1027 @copy /Y nul: headers
1028
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -505,10 +505,11 @@
505505
$(SRCDIR)/purge.c \
506506
$(SRCDIR)/rebuild.c \
507507
$(SRCDIR)/regexp.c \
508508
$(SRCDIR)/repolist.c \
509509
$(SRCDIR)/report.c \
510
+ $(SRCDIR)/robot.c \
510511
$(SRCDIR)/rss.c \
511512
$(SRCDIR)/schema.c \
512513
$(SRCDIR)/search.c \
513514
$(SRCDIR)/security_audit.c \
514515
$(SRCDIR)/setup.c \
@@ -622,10 +623,11 @@
622623
$(SRCDIR)/fossil.page.chat.js \
623624
$(SRCDIR)/fossil.page.fileedit.js \
624625
$(SRCDIR)/fossil.page.forumpost.js \
625626
$(SRCDIR)/fossil.page.pikchrshow.js \
626627
$(SRCDIR)/fossil.page.pikchrshowasm.js \
628
+ $(SRCDIR)/fossil.page.ticket.js \
627629
$(SRCDIR)/fossil.page.whistory.js \
628630
$(SRCDIR)/fossil.page.wikiedit.js \
629631
$(SRCDIR)/fossil.pikchr.js \
630632
$(SRCDIR)/fossil.popupwidget.js \
631633
$(SRCDIR)/fossil.storage.js \
@@ -771,10 +773,11 @@
771773
$(OBJDIR)/purge_.c \
772774
$(OBJDIR)/rebuild_.c \
773775
$(OBJDIR)/regexp_.c \
774776
$(OBJDIR)/repolist_.c \
775777
$(OBJDIR)/report_.c \
778
+ $(OBJDIR)/robot_.c \
776779
$(OBJDIR)/rss_.c \
777780
$(OBJDIR)/schema_.c \
778781
$(OBJDIR)/search_.c \
779782
$(OBJDIR)/security_audit_.c \
780783
$(OBJDIR)/setup_.c \
@@ -921,10 +924,11 @@
921924
$(OBJDIR)/purge.o \
922925
$(OBJDIR)/rebuild.o \
923926
$(OBJDIR)/regexp.o \
924927
$(OBJDIR)/repolist.o \
925928
$(OBJDIR)/report.o \
929
+ $(OBJDIR)/robot.o \
926930
$(OBJDIR)/rss.o \
927931
$(OBJDIR)/schema.o \
928932
$(OBJDIR)/search.o \
929933
$(OBJDIR)/security_audit.o \
930934
$(OBJDIR)/setup.o \
@@ -1275,10 +1279,11 @@
12751279
$(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
12761280
$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
12771281
$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
12781282
$(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
12791283
$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
1284
+ $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
12801285
$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
12811286
$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
12821287
$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
12831288
$(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
12841289
$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -2167,10 +2172,18 @@
21672172
21682173
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
21692174
$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
21702175
21712176
$(OBJDIR)/report.h: $(OBJDIR)/headers
2177
+
2178
+$(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(TRANSLATE)
2179
+ $(TRANSLATE) $(SRCDIR)/robot.c >$@
2180
+
2181
+$(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
2182
+ $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
2183
+
2184
+$(OBJDIR)/robot.h: $(OBJDIR)/headers
21722185
21732186
$(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE)
21742187
$(TRANSLATE) $(SRCDIR)/rss.c >$@
21752188
21762189
$(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
21772190
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -505,10 +505,11 @@
505 $(SRCDIR)/purge.c \
506 $(SRCDIR)/rebuild.c \
507 $(SRCDIR)/regexp.c \
508 $(SRCDIR)/repolist.c \
509 $(SRCDIR)/report.c \
 
510 $(SRCDIR)/rss.c \
511 $(SRCDIR)/schema.c \
512 $(SRCDIR)/search.c \
513 $(SRCDIR)/security_audit.c \
514 $(SRCDIR)/setup.c \
@@ -622,10 +623,11 @@
622 $(SRCDIR)/fossil.page.chat.js \
623 $(SRCDIR)/fossil.page.fileedit.js \
624 $(SRCDIR)/fossil.page.forumpost.js \
625 $(SRCDIR)/fossil.page.pikchrshow.js \
626 $(SRCDIR)/fossil.page.pikchrshowasm.js \
 
627 $(SRCDIR)/fossil.page.whistory.js \
628 $(SRCDIR)/fossil.page.wikiedit.js \
629 $(SRCDIR)/fossil.pikchr.js \
630 $(SRCDIR)/fossil.popupwidget.js \
631 $(SRCDIR)/fossil.storage.js \
@@ -771,10 +773,11 @@
771 $(OBJDIR)/purge_.c \
772 $(OBJDIR)/rebuild_.c \
773 $(OBJDIR)/regexp_.c \
774 $(OBJDIR)/repolist_.c \
775 $(OBJDIR)/report_.c \
 
776 $(OBJDIR)/rss_.c \
777 $(OBJDIR)/schema_.c \
778 $(OBJDIR)/search_.c \
779 $(OBJDIR)/security_audit_.c \
780 $(OBJDIR)/setup_.c \
@@ -921,10 +924,11 @@
921 $(OBJDIR)/purge.o \
922 $(OBJDIR)/rebuild.o \
923 $(OBJDIR)/regexp.o \
924 $(OBJDIR)/repolist.o \
925 $(OBJDIR)/report.o \
 
926 $(OBJDIR)/rss.o \
927 $(OBJDIR)/schema.o \
928 $(OBJDIR)/search.o \
929 $(OBJDIR)/security_audit.o \
930 $(OBJDIR)/setup.o \
@@ -1275,10 +1279,11 @@
1275 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
1276 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
1277 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
1278 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
1279 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
 
1280 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
1281 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
1282 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
1283 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
1284 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -2167,10 +2172,18 @@
2167
2168 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
2169 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
2170
2171 $(OBJDIR)/report.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
2172
2173 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE)
2174 $(TRANSLATE) $(SRCDIR)/rss.c >$@
2175
2176 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
2177
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -505,10 +505,11 @@
505 $(SRCDIR)/purge.c \
506 $(SRCDIR)/rebuild.c \
507 $(SRCDIR)/regexp.c \
508 $(SRCDIR)/repolist.c \
509 $(SRCDIR)/report.c \
510 $(SRCDIR)/robot.c \
511 $(SRCDIR)/rss.c \
512 $(SRCDIR)/schema.c \
513 $(SRCDIR)/search.c \
514 $(SRCDIR)/security_audit.c \
515 $(SRCDIR)/setup.c \
@@ -622,10 +623,11 @@
623 $(SRCDIR)/fossil.page.chat.js \
624 $(SRCDIR)/fossil.page.fileedit.js \
625 $(SRCDIR)/fossil.page.forumpost.js \
626 $(SRCDIR)/fossil.page.pikchrshow.js \
627 $(SRCDIR)/fossil.page.pikchrshowasm.js \
628 $(SRCDIR)/fossil.page.ticket.js \
629 $(SRCDIR)/fossil.page.whistory.js \
630 $(SRCDIR)/fossil.page.wikiedit.js \
631 $(SRCDIR)/fossil.pikchr.js \
632 $(SRCDIR)/fossil.popupwidget.js \
633 $(SRCDIR)/fossil.storage.js \
@@ -771,10 +773,11 @@
773 $(OBJDIR)/purge_.c \
774 $(OBJDIR)/rebuild_.c \
775 $(OBJDIR)/regexp_.c \
776 $(OBJDIR)/repolist_.c \
777 $(OBJDIR)/report_.c \
778 $(OBJDIR)/robot_.c \
779 $(OBJDIR)/rss_.c \
780 $(OBJDIR)/schema_.c \
781 $(OBJDIR)/search_.c \
782 $(OBJDIR)/security_audit_.c \
783 $(OBJDIR)/setup_.c \
@@ -921,10 +924,11 @@
924 $(OBJDIR)/purge.o \
925 $(OBJDIR)/rebuild.o \
926 $(OBJDIR)/regexp.o \
927 $(OBJDIR)/repolist.o \
928 $(OBJDIR)/report.o \
929 $(OBJDIR)/robot.o \
930 $(OBJDIR)/rss.o \
931 $(OBJDIR)/schema.o \
932 $(OBJDIR)/search.o \
933 $(OBJDIR)/security_audit.o \
934 $(OBJDIR)/setup.o \
@@ -1275,10 +1279,11 @@
1279 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
1280 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
1281 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
1282 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
1283 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
1284 $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
1285 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
1286 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
1287 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
1288 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
1289 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -2167,10 +2172,18 @@
2172
2173 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
2174 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
2175
2176 $(OBJDIR)/report.h: $(OBJDIR)/headers
2177
2178 $(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(TRANSLATE)
2179 $(TRANSLATE) $(SRCDIR)/robot.c >$@
2180
2181 $(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
2182 $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
2183
2184 $(OBJDIR)/robot.h: $(OBJDIR)/headers
2185
2186 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE)
2187 $(TRANSLATE) $(SRCDIR)/rss.c >$@
2188
2189 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
2190
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -467,10 +467,11 @@
467467
"$(OX)\purge_.c" \
468468
"$(OX)\rebuild_.c" \
469469
"$(OX)\regexp_.c" \
470470
"$(OX)\repolist_.c" \
471471
"$(OX)\report_.c" \
472
+ "$(OX)\robot_.c" \
472473
"$(OX)\rss_.c" \
473474
"$(OX)\schema_.c" \
474475
"$(OX)\search_.c" \
475476
"$(OX)\security_audit_.c" \
476477
"$(OX)\setup_.c" \
@@ -584,10 +585,11 @@
584585
"$(SRCDIR)\fossil.page.chat.js" \
585586
"$(SRCDIR)\fossil.page.fileedit.js" \
586587
"$(SRCDIR)\fossil.page.forumpost.js" \
587588
"$(SRCDIR)\fossil.page.pikchrshow.js" \
588589
"$(SRCDIR)\fossil.page.pikchrshowasm.js" \
590
+ "$(SRCDIR)\fossil.page.ticket.js" \
589591
"$(SRCDIR)\fossil.page.whistory.js" \
590592
"$(SRCDIR)\fossil.page.wikiedit.js" \
591593
"$(SRCDIR)\fossil.pikchr.js" \
592594
"$(SRCDIR)\fossil.popupwidget.js" \
593595
"$(SRCDIR)\fossil.storage.js" \
@@ -734,10 +736,11 @@
734736
"$(OX)\purge$O" \
735737
"$(OX)\rebuild$O" \
736738
"$(OX)\regexp$O" \
737739
"$(OX)\repolist$O" \
738740
"$(OX)\report$O" \
741
+ "$(OX)\robot$O" \
739742
"$(OX)\rss$O" \
740743
"$(OX)\schema$O" \
741744
"$(OX)\search$O" \
742745
"$(OX)\security_audit$O" \
743746
"$(OX)\setup$O" \
@@ -984,10 +987,11 @@
984987
echo "$(OX)\purge.obj" >> $@
985988
echo "$(OX)\rebuild.obj" >> $@
986989
echo "$(OX)\regexp.obj" >> $@
987990
echo "$(OX)\repolist.obj" >> $@
988991
echo "$(OX)\report.obj" >> $@
992
+ echo "$(OX)\robot.obj" >> $@
989993
echo "$(OX)\rss.obj" >> $@
990994
echo "$(OX)\schema.obj" >> $@
991995
echo "$(OX)\search.obj" >> $@
992996
echo "$(OX)\security_audit.obj" >> $@
993997
echo "$(OX)\setup.obj" >> $@
@@ -1217,10 +1221,11 @@
12171221
echo "$(SRCDIR)\fossil.page.chat.js" >> $@
12181222
echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
12191223
echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
12201224
echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
12211225
echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@
1226
+ echo "$(SRCDIR)\fossil.page.ticket.js" >> $@
12221227
echo "$(SRCDIR)\fossil.page.whistory.js" >> $@
12231228
echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
12241229
echo "$(SRCDIR)\fossil.pikchr.js" >> $@
12251230
echo "$(SRCDIR)\fossil.popupwidget.js" >> $@
12261231
echo "$(SRCDIR)\fossil.storage.js" >> $@
@@ -1889,10 +1894,16 @@
18891894
"$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
18901895
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"
18911896
18921897
"$(OX)\report_.c" : "$(SRCDIR)\report.c"
18931898
"$(OBJDIR)\translate$E" $** > $@
1899
+
1900
+"$(OX)\robot$O" : "$(OX)\robot_.c" "$(OX)\robot.h"
1901
+ $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\robot_.c"
1902
+
1903
+"$(OX)\robot_.c" : "$(SRCDIR)\robot.c"
1904
+ "$(OBJDIR)\translate$E" $** > $@
18941905
18951906
"$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
18961907
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"
18971908
18981909
"$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
@@ -2257,10 +2268,11 @@
22572268
"$(OX)\purge_.c":"$(OX)\purge.h" \
22582269
"$(OX)\rebuild_.c":"$(OX)\rebuild.h" \
22592270
"$(OX)\regexp_.c":"$(OX)\regexp.h" \
22602271
"$(OX)\repolist_.c":"$(OX)\repolist.h" \
22612272
"$(OX)\report_.c":"$(OX)\report.h" \
2273
+ "$(OX)\robot_.c":"$(OX)\robot.h" \
22622274
"$(OX)\rss_.c":"$(OX)\rss.h" \
22632275
"$(OX)\schema_.c":"$(OX)\schema.h" \
22642276
"$(OX)\search_.c":"$(OX)\search.h" \
22652277
"$(OX)\security_audit_.c":"$(OX)\security_audit.h" \
22662278
"$(OX)\setup_.c":"$(OX)\setup.h" \
22672279
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -467,10 +467,11 @@
467 "$(OX)\purge_.c" \
468 "$(OX)\rebuild_.c" \
469 "$(OX)\regexp_.c" \
470 "$(OX)\repolist_.c" \
471 "$(OX)\report_.c" \
 
472 "$(OX)\rss_.c" \
473 "$(OX)\schema_.c" \
474 "$(OX)\search_.c" \
475 "$(OX)\security_audit_.c" \
476 "$(OX)\setup_.c" \
@@ -584,10 +585,11 @@
584 "$(SRCDIR)\fossil.page.chat.js" \
585 "$(SRCDIR)\fossil.page.fileedit.js" \
586 "$(SRCDIR)\fossil.page.forumpost.js" \
587 "$(SRCDIR)\fossil.page.pikchrshow.js" \
588 "$(SRCDIR)\fossil.page.pikchrshowasm.js" \
 
589 "$(SRCDIR)\fossil.page.whistory.js" \
590 "$(SRCDIR)\fossil.page.wikiedit.js" \
591 "$(SRCDIR)\fossil.pikchr.js" \
592 "$(SRCDIR)\fossil.popupwidget.js" \
593 "$(SRCDIR)\fossil.storage.js" \
@@ -734,10 +736,11 @@
734 "$(OX)\purge$O" \
735 "$(OX)\rebuild$O" \
736 "$(OX)\regexp$O" \
737 "$(OX)\repolist$O" \
738 "$(OX)\report$O" \
 
739 "$(OX)\rss$O" \
740 "$(OX)\schema$O" \
741 "$(OX)\search$O" \
742 "$(OX)\security_audit$O" \
743 "$(OX)\setup$O" \
@@ -984,10 +987,11 @@
984 echo "$(OX)\purge.obj" >> $@
985 echo "$(OX)\rebuild.obj" >> $@
986 echo "$(OX)\regexp.obj" >> $@
987 echo "$(OX)\repolist.obj" >> $@
988 echo "$(OX)\report.obj" >> $@
 
989 echo "$(OX)\rss.obj" >> $@
990 echo "$(OX)\schema.obj" >> $@
991 echo "$(OX)\search.obj" >> $@
992 echo "$(OX)\security_audit.obj" >> $@
993 echo "$(OX)\setup.obj" >> $@
@@ -1217,10 +1221,11 @@
1217 echo "$(SRCDIR)\fossil.page.chat.js" >> $@
1218 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1219 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1220 echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
1221 echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@
 
1222 echo "$(SRCDIR)\fossil.page.whistory.js" >> $@
1223 echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
1224 echo "$(SRCDIR)\fossil.pikchr.js" >> $@
1225 echo "$(SRCDIR)\fossil.popupwidget.js" >> $@
1226 echo "$(SRCDIR)\fossil.storage.js" >> $@
@@ -1889,10 +1894,16 @@
1889 "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
1890 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"
1891
1892 "$(OX)\report_.c" : "$(SRCDIR)\report.c"
1893 "$(OBJDIR)\translate$E" $** > $@
 
 
 
 
 
 
1894
1895 "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
1896 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"
1897
1898 "$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
@@ -2257,10 +2268,11 @@
2257 "$(OX)\purge_.c":"$(OX)\purge.h" \
2258 "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \
2259 "$(OX)\regexp_.c":"$(OX)\regexp.h" \
2260 "$(OX)\repolist_.c":"$(OX)\repolist.h" \
2261 "$(OX)\report_.c":"$(OX)\report.h" \
 
2262 "$(OX)\rss_.c":"$(OX)\rss.h" \
2263 "$(OX)\schema_.c":"$(OX)\schema.h" \
2264 "$(OX)\search_.c":"$(OX)\search.h" \
2265 "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \
2266 "$(OX)\setup_.c":"$(OX)\setup.h" \
2267
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -467,10 +467,11 @@
467 "$(OX)\purge_.c" \
468 "$(OX)\rebuild_.c" \
469 "$(OX)\regexp_.c" \
470 "$(OX)\repolist_.c" \
471 "$(OX)\report_.c" \
472 "$(OX)\robot_.c" \
473 "$(OX)\rss_.c" \
474 "$(OX)\schema_.c" \
475 "$(OX)\search_.c" \
476 "$(OX)\security_audit_.c" \
477 "$(OX)\setup_.c" \
@@ -584,10 +585,11 @@
585 "$(SRCDIR)\fossil.page.chat.js" \
586 "$(SRCDIR)\fossil.page.fileedit.js" \
587 "$(SRCDIR)\fossil.page.forumpost.js" \
588 "$(SRCDIR)\fossil.page.pikchrshow.js" \
589 "$(SRCDIR)\fossil.page.pikchrshowasm.js" \
590 "$(SRCDIR)\fossil.page.ticket.js" \
591 "$(SRCDIR)\fossil.page.whistory.js" \
592 "$(SRCDIR)\fossil.page.wikiedit.js" \
593 "$(SRCDIR)\fossil.pikchr.js" \
594 "$(SRCDIR)\fossil.popupwidget.js" \
595 "$(SRCDIR)\fossil.storage.js" \
@@ -734,10 +736,11 @@
736 "$(OX)\purge$O" \
737 "$(OX)\rebuild$O" \
738 "$(OX)\regexp$O" \
739 "$(OX)\repolist$O" \
740 "$(OX)\report$O" \
741 "$(OX)\robot$O" \
742 "$(OX)\rss$O" \
743 "$(OX)\schema$O" \
744 "$(OX)\search$O" \
745 "$(OX)\security_audit$O" \
746 "$(OX)\setup$O" \
@@ -984,10 +987,11 @@
987 echo "$(OX)\purge.obj" >> $@
988 echo "$(OX)\rebuild.obj" >> $@
989 echo "$(OX)\regexp.obj" >> $@
990 echo "$(OX)\repolist.obj" >> $@
991 echo "$(OX)\report.obj" >> $@
992 echo "$(OX)\robot.obj" >> $@
993 echo "$(OX)\rss.obj" >> $@
994 echo "$(OX)\schema.obj" >> $@
995 echo "$(OX)\search.obj" >> $@
996 echo "$(OX)\security_audit.obj" >> $@
997 echo "$(OX)\setup.obj" >> $@
@@ -1217,10 +1221,11 @@
1221 echo "$(SRCDIR)\fossil.page.chat.js" >> $@
1222 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1223 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1224 echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
1225 echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@
1226 echo "$(SRCDIR)\fossil.page.ticket.js" >> $@
1227 echo "$(SRCDIR)\fossil.page.whistory.js" >> $@
1228 echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
1229 echo "$(SRCDIR)\fossil.pikchr.js" >> $@
1230 echo "$(SRCDIR)\fossil.popupwidget.js" >> $@
1231 echo "$(SRCDIR)\fossil.storage.js" >> $@
@@ -1889,10 +1894,16 @@
1894 "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
1895 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"
1896
1897 "$(OX)\report_.c" : "$(SRCDIR)\report.c"
1898 "$(OBJDIR)\translate$E" $** > $@
1899
1900 "$(OX)\robot$O" : "$(OX)\robot_.c" "$(OX)\robot.h"
1901 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\robot_.c"
1902
1903 "$(OX)\robot_.c" : "$(SRCDIR)\robot.c"
1904 "$(OBJDIR)\translate$E" $** > $@
1905
1906 "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
1907 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"
1908
1909 "$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
@@ -2257,10 +2268,11 @@
2268 "$(OX)\purge_.c":"$(OX)\purge.h" \
2269 "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \
2270 "$(OX)\regexp_.c":"$(OX)\regexp.h" \
2271 "$(OX)\repolist_.c":"$(OX)\repolist.h" \
2272 "$(OX)\report_.c":"$(OX)\report.h" \
2273 "$(OX)\robot_.c":"$(OX)\robot.h" \
2274 "$(OX)\rss_.c":"$(OX)\rss.h" \
2275 "$(OX)\schema_.c":"$(OX)\schema.h" \
2276 "$(OX)\search_.c":"$(OX)\search.h" \
2277 "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \
2278 "$(OX)\setup_.c":"$(OX)\setup.h" \
2279
--- www/aboutcgi.wiki
+++ www/aboutcgi.wiki
@@ -131,11 +131,11 @@
131131
132132
Usually, the webpage being requested is the first term of the
133133
PATH_INFO environment variable. (Exceptions to this rule are noted
134134
in the sequel.) For our example, the first term of PATH_INFO
135135
is "timeline", which means that Fossil will generate
136
-the [/help?cmd=/timeline|/timeline] webpage.
136
+the [/help/www/timeline|/timeline] webpage.
137137
138138
With Fossil, terms of PATH_INFO beyond the webpage name are converted into
139139
the "name" query parameter. Hence, the following two URLs mean
140140
exactly the same thing to Fossil:
141141
<ol type='A'>
@@ -143,11 +143,11 @@
143143
<li> [https://fossil-scm.org/home/info?name=c14ecc43]
144144
</ol>
145145
146146
In both cases, the CGI script is called "/fossil". For case (A),
147147
the PATH_INFO variable will be "info/c14ecc43" and so the
148
-"[/help?cmd=/info|/info]" webpage will be generated and the suffix of
148
+"[/help/www/info|/info]" webpage will be generated and the suffix of
149149
PATH_INFO will be converted into the "name" query parameter, which
150150
identifies the artifact about which information is requested.
151151
In case (B), the PATH_INFO is just "info", but the same "name"
152152
query parameter is set explicitly by the URL itself.
153153
@@ -282,11 +282,11 @@
282282
can then reference various properties values.
283283
Fossil does not care where the value of each property comes from (POST
284284
content, cookies, or query parameters) only that the property exists
285285
and has a value.</p></li>
286286
<li><p>
287
-The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands
287
+The "[/help/ui|fossil ui]" and "[/help/server|fossil server]" commands
288288
are implemented using a simple built-in web server that accepts incoming HTTP
289289
requests, translates each request into a CGI invocation, then creates a
290290
separate child Fossil process to handle each request. In other words, CGI
291291
is used internally to implement "fossil ui/server".
292292
<br><br>
293293
--- www/aboutcgi.wiki
+++ www/aboutcgi.wiki
@@ -131,11 +131,11 @@
131
132 Usually, the webpage being requested is the first term of the
133 PATH_INFO environment variable. (Exceptions to this rule are noted
134 in the sequel.) For our example, the first term of PATH_INFO
135 is "timeline", which means that Fossil will generate
136 the [/help?cmd=/timeline|/timeline] webpage.
137
138 With Fossil, terms of PATH_INFO beyond the webpage name are converted into
139 the "name" query parameter. Hence, the following two URLs mean
140 exactly the same thing to Fossil:
141 <ol type='A'>
@@ -143,11 +143,11 @@
143 <li> [https://fossil-scm.org/home/info?name=c14ecc43]
144 </ol>
145
146 In both cases, the CGI script is called "/fossil". For case (A),
147 the PATH_INFO variable will be "info/c14ecc43" and so the
148 "[/help?cmd=/info|/info]" webpage will be generated and the suffix of
149 PATH_INFO will be converted into the "name" query parameter, which
150 identifies the artifact about which information is requested.
151 In case (B), the PATH_INFO is just "info", but the same "name"
152 query parameter is set explicitly by the URL itself.
153
@@ -282,11 +282,11 @@
282 can then reference various properties values.
283 Fossil does not care where the value of each property comes from (POST
284 content, cookies, or query parameters) only that the property exists
285 and has a value.</p></li>
286 <li><p>
287 The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands
288 are implemented using a simple built-in web server that accepts incoming HTTP
289 requests, translates each request into a CGI invocation, then creates a
290 separate child Fossil process to handle each request. In other words, CGI
291 is used internally to implement "fossil ui/server".
292 <br><br>
293
--- www/aboutcgi.wiki
+++ www/aboutcgi.wiki
@@ -131,11 +131,11 @@
131
132 Usually, the webpage being requested is the first term of the
133 PATH_INFO environment variable. (Exceptions to this rule are noted
134 in the sequel.) For our example, the first term of PATH_INFO
135 is "timeline", which means that Fossil will generate
136 the [/help/www/timeline|/timeline] webpage.
137
138 With Fossil, terms of PATH_INFO beyond the webpage name are converted into
139 the "name" query parameter. Hence, the following two URLs mean
140 exactly the same thing to Fossil:
141 <ol type='A'>
@@ -143,11 +143,11 @@
143 <li> [https://fossil-scm.org/home/info?name=c14ecc43]
144 </ol>
145
146 In both cases, the CGI script is called "/fossil". For case (A),
147 the PATH_INFO variable will be "info/c14ecc43" and so the
148 "[/help/www/info|/info]" webpage will be generated and the suffix of
149 PATH_INFO will be converted into the "name" query parameter, which
150 identifies the artifact about which information is requested.
151 In case (B), the PATH_INFO is just "info", but the same "name"
152 query parameter is set explicitly by the URL itself.
153
@@ -282,11 +282,11 @@
282 can then reference various properties values.
283 Fossil does not care where the value of each property comes from (POST
284 content, cookies, or query parameters) only that the property exists
285 and has a value.</p></li>
286 <li><p>
287 The "[/help/ui|fossil ui]" and "[/help/server|fossil server]" commands
288 are implemented using a simple built-in web server that accepts incoming HTTP
289 requests, translates each request into a CGI invocation, then creates a
290 separate child Fossil process to handle each request. In other words, CGI
291 is used internally to implement "fossil ui/server".
292 <br><br>
293
--- www/aboutdownload.wiki
+++ www/aboutdownload.wiki
@@ -4,29 +4,29 @@
44
55
The [/uv/download.html|Download] page for the Fossil self-hosting
66
repository is implemented using [./unvers.wiki|unversioned files].
77
The "download.html" screen itself, and the various build products
88
are all stored as unversioned content. The download.html page
9
-uses XMLHttpRequest() to retrieve the [/help?cmd=/juvlist|/juvlist] webpage
9
+uses XMLHttpRequest() to retrieve the [/help/www/juvlist|/juvlist] webpage
1010
for a list of all unversioned files. Javascript in the
1111
[/uv/download.js?mimetype=text/plain|download.js] file (which is
1212
sourced by "download.html") then figures out which unversioned files are
1313
build products and paints appropriate icons on the displayed
1414
download page.
1515
1616
Except, the "Source Tarball" download products are not stored as
1717
unversioned files. They are computed on-demand by the
18
-[/help?cmd=/tarball|/tarball web page].
18
+[/help/www/tarball|/tarball web page].
1919
2020
When a new version is generated, the developers use the
21
-[/help?cmd=uv|fossil uv edit] command to make minor changes
21
+[/help/uv|fossil uv edit] command to make minor changes
2222
to the "[/uv/download.js?mimetype=text/plain|download.js]"
2323
file so that it knows about the
2424
new version number. Then the developers run
25
-the [/help?cmd=uv|fossil uv add] command for each
25
+the [/help/uv|fossil uv add] command for each
2626
build product. Finally, the
27
-[/help?cmd=uv|fossil uv sync] command is run to push all
27
+[/help/uv|fossil uv sync] command is run to push all
2828
the content up to servers. All
2929
[./selfhost.wiki|three self-hosting repositories] for Fossil
3030
are updated automatically.
3131
3232
<h2>2.0 Details</h2>
@@ -51,11 +51,11 @@
5151
"[./embeddeddoc.wiki|embedded documentation]" for further details on
5252
how this &lt;div class='fossil-doc'&gt; markup works.
5353
5454
With each new release, the "releases" variable in the javascript on
5555
the [/uv/download.js?mimetype=text/plain|download.js] page is
56
-edited (using "[/help?cmd=uv|fossil uv edit download.js]") to add
56
+edited (using "[/help/uv|fossil uv edit download.js]") to add
5757
details of the release.
5858
5959
When the JavaScript in the "download.js" file runs, it requests
6060
a listing of all unversioned content using the /juvlist URL.
6161
([/juvlist|sample /juvlist output]). The content of the download page is
@@ -65,25 +65,25 @@
6565
Build products need to be constructed on different machines. The precompiled
6666
binary for Linux is compiled on Linux, the precompiled binary for Windows
6767
is compiled on Windows11, and so forth. After a new release is tagged,
6868
the release manager goes around to each of the target platforms, checks
6969
out the release and compiles it, then runs
70
-[/help?cmd=uv|fossil uv add] for the build product followed by
71
-[/help?cmd=uv|fossil uv sync] to push the new build product to the
70
+[/help/uv|fossil uv add] for the build product followed by
71
+[/help/uv|fossil uv sync] to push the new build product to the
7272
[./selfhost.wiki|various servers]. This process is repeated for
7373
each build product.
7474
7575
When older builds are retired from the download page, the
7676
[/uv/download.js?mimetype=text/plain|download.js] page is again
7777
edited to remove the corresponding entry from the "release" variable
7878
and the edit is synced using
79
-[/help?cmd=uv|fossil uv sync]. This causes the build products to
79
+[/help/uv|fossil uv sync]. This causes the build products to
8080
disappear from the download page immediately. But those build products
8181
are still taking up space in the unversioned content table of the
8282
server repository. To purge the obsolete build products, one or
83
-more [/help?cmd=uv|fossil uv rm] commands are run, followed by
84
-another [/help?cmd=uv|fossil uv sync]. It is important to purge
83
+more [/help/uv|fossil uv rm] commands are run, followed by
84
+another [/help/uv|fossil uv sync]. It is important to purge
8585
obsolete build products since they take up a lot of space.
8686
At [/repo-tabsize] you can see that the unversioned table takes up
8787
a substantial fraction of the repository.
8888
8989
<h2>3.0 Security</h2>
9090
--- www/aboutdownload.wiki
+++ www/aboutdownload.wiki
@@ -4,29 +4,29 @@
4
5 The [/uv/download.html|Download] page for the Fossil self-hosting
6 repository is implemented using [./unvers.wiki|unversioned files].
7 The "download.html" screen itself, and the various build products
8 are all stored as unversioned content. The download.html page
9 uses XMLHttpRequest() to retrieve the [/help?cmd=/juvlist|/juvlist] webpage
10 for a list of all unversioned files. Javascript in the
11 [/uv/download.js?mimetype=text/plain|download.js] file (which is
12 sourced by "download.html") then figures out which unversioned files are
13 build products and paints appropriate icons on the displayed
14 download page.
15
16 Except, the "Source Tarball" download products are not stored as
17 unversioned files. They are computed on-demand by the
18 [/help?cmd=/tarball|/tarball web page].
19
20 When a new version is generated, the developers use the
21 [/help?cmd=uv|fossil uv edit] command to make minor changes
22 to the "[/uv/download.js?mimetype=text/plain|download.js]"
23 file so that it knows about the
24 new version number. Then the developers run
25 the [/help?cmd=uv|fossil uv add] command for each
26 build product. Finally, the
27 [/help?cmd=uv|fossil uv sync] command is run to push all
28 the content up to servers. All
29 [./selfhost.wiki|three self-hosting repositories] for Fossil
30 are updated automatically.
31
32 <h2>2.0 Details</h2>
@@ -51,11 +51,11 @@
51 "[./embeddeddoc.wiki|embedded documentation]" for further details on
52 how this &lt;div class='fossil-doc'&gt; markup works.
53
54 With each new release, the "releases" variable in the javascript on
55 the [/uv/download.js?mimetype=text/plain|download.js] page is
56 edited (using "[/help?cmd=uv|fossil uv edit download.js]") to add
57 details of the release.
58
59 When the JavaScript in the "download.js" file runs, it requests
60 a listing of all unversioned content using the /juvlist URL.
61 ([/juvlist|sample /juvlist output]). The content of the download page is
@@ -65,25 +65,25 @@
65 Build products need to be constructed on different machines. The precompiled
66 binary for Linux is compiled on Linux, the precompiled binary for Windows
67 is compiled on Windows11, and so forth. After a new release is tagged,
68 the release manager goes around to each of the target platforms, checks
69 out the release and compiles it, then runs
70 [/help?cmd=uv|fossil uv add] for the build product followed by
71 [/help?cmd=uv|fossil uv sync] to push the new build product to the
72 [./selfhost.wiki|various servers]. This process is repeated for
73 each build product.
74
75 When older builds are retired from the download page, the
76 [/uv/download.js?mimetype=text/plain|download.js] page is again
77 edited to remove the corresponding entry from the "release" variable
78 and the edit is synced using
79 [/help?cmd=uv|fossil uv sync]. This causes the build products to
80 disappear from the download page immediately. But those build products
81 are still taking up space in the unversioned content table of the
82 server repository. To purge the obsolete build products, one or
83 more [/help?cmd=uv|fossil uv rm] commands are run, followed by
84 another [/help?cmd=uv|fossil uv sync]. It is important to purge
85 obsolete build products since they take up a lot of space.
86 At [/repo-tabsize] you can see that the unversioned table takes up
87 a substantial fraction of the repository.
88
89 <h2>3.0 Security</h2>
90
--- www/aboutdownload.wiki
+++ www/aboutdownload.wiki
@@ -4,29 +4,29 @@
4
5 The [/uv/download.html|Download] page for the Fossil self-hosting
6 repository is implemented using [./unvers.wiki|unversioned files].
7 The "download.html" screen itself, and the various build products
8 are all stored as unversioned content. The download.html page
9 uses XMLHttpRequest() to retrieve the [/help/www/juvlist|/juvlist] webpage
10 for a list of all unversioned files. Javascript in the
11 [/uv/download.js?mimetype=text/plain|download.js] file (which is
12 sourced by "download.html") then figures out which unversioned files are
13 build products and paints appropriate icons on the displayed
14 download page.
15
16 Except, the "Source Tarball" download products are not stored as
17 unversioned files. They are computed on-demand by the
18 [/help/www/tarball|/tarball web page].
19
20 When a new version is generated, the developers use the
21 [/help/uv|fossil uv edit] command to make minor changes
22 to the "[/uv/download.js?mimetype=text/plain|download.js]"
23 file so that it knows about the
24 new version number. Then the developers run
25 the [/help/uv|fossil uv add] command for each
26 build product. Finally, the
27 [/help/uv|fossil uv sync] command is run to push all
28 the content up to servers. All
29 [./selfhost.wiki|three self-hosting repositories] for Fossil
30 are updated automatically.
31
32 <h2>2.0 Details</h2>
@@ -51,11 +51,11 @@
51 "[./embeddeddoc.wiki|embedded documentation]" for further details on
52 how this &lt;div class='fossil-doc'&gt; markup works.
53
54 With each new release, the "releases" variable in the javascript on
55 the [/uv/download.js?mimetype=text/plain|download.js] page is
56 edited (using "[/help/uv|fossil uv edit download.js]") to add
57 details of the release.
58
59 When the JavaScript in the "download.js" file runs, it requests
60 a listing of all unversioned content using the /juvlist URL.
61 ([/juvlist|sample /juvlist output]). The content of the download page is
@@ -65,25 +65,25 @@
65 Build products need to be constructed on different machines. The precompiled
66 binary for Linux is compiled on Linux, the precompiled binary for Windows
67 is compiled on Windows11, and so forth. After a new release is tagged,
68 the release manager goes around to each of the target platforms, checks
69 out the release and compiles it, then runs
70 [/help/uv|fossil uv add] for the build product followed by
71 [/help/uv|fossil uv sync] to push the new build product to the
72 [./selfhost.wiki|various servers]. This process is repeated for
73 each build product.
74
75 When older builds are retired from the download page, the
76 [/uv/download.js?mimetype=text/plain|download.js] page is again
77 edited to remove the corresponding entry from the "release" variable
78 and the edit is synced using
79 [/help/uv|fossil uv sync]. This causes the build products to
80 disappear from the download page immediately. But those build products
81 are still taking up space in the unversioned content table of the
82 server repository. To purge the obsolete build products, one or
83 more [/help/uv|fossil uv rm] commands are run, followed by
84 another [/help/uv|fossil uv sync]. It is important to purge
85 obsolete build products since they take up a lot of space.
86 At [/repo-tabsize] you can see that the unversioned table takes up
87 a substantial fraction of the repository.
88
89 <h2>3.0 Security</h2>
90
+11 -11
--- www/alerts.md
+++ www/alerts.md
@@ -3,11 +3,11 @@
33
## Overview
44
55
Beginning with version 2.7, Fossil can send email messages to
66
subscribers to alert them to changes in the repository:
77
8
- * New [checkins](/help?cmd=ci)
8
+ * New [checkins](/help/ci)
99
* [Ticket](./tickets.wiki) changes
1010
* [Wiki](./wikitheory.wiki) page changes
1111
* New and edited [forum](./forum.wiki) posts
1212
* Users receiving [new permissions](./caps/index.md) (admins only)
1313
* Announcements
@@ -541,26 +541,26 @@
541541
control the email alert system, some of which have not been mentioned so
542542
far:
543543
544544
Commands:
545545
546
- * The [`alerts`](/help?cmd=alerts) command
547
- * The [`test-alert`](/help?cmd=test-alert) command
548
- * The [`test-add-alerts`](/help?cmd=test-add-alerts) command
546
+ * The [`alerts`](/help/alerts) command
547
+ * The [`test-alert`](/help/test-alert) command
548
+ * The [`test-add-alerts`](/help/test-add-alerts) command
549549
550550
Web pages available to users and subscribers:
551551
552
- * The [`/subscribe`](/help?cmd=/subscribe) page
553
- * The [`/alerts`](/help?cmd=/alerts) page
554
- * The [`/unsubscribe`](/help?cmd=/unsubscribe) page
555
- * The [`/renew`](/help?cmd=/renew) page
556
- * The [`/contact_admin`](/help?cmd=/contact_admin) page
552
+ * The [`/subscribe`](/help/www/subscribe) page
553
+ * The [`/alerts`](/help/www/alerts) page
554
+ * The [`/unsubscribe`](/help/www/unsubscribe) page
555
+ * The [`/renew`](/help/www/renew) page
556
+ * The [`/contact_admin`](/help/www/contact_admin) page
557557
558558
Administrator-only web pages:
559559
560
- * The [`/setup_notification`](/help?cmd=/setup_notification) page
561
- * The [`/subscribers`](/help?cmd=/subscribers) page
560
+ * The [`/setup_notification`](/help/www/setup_notification) page
561
+ * The [`/subscribers`](/help/www/subscribers) page
562562
563563
564564
<a id="design"></a>
565565
## Design of Email Alerts
566566
567567
--- www/alerts.md
+++ www/alerts.md
@@ -3,11 +3,11 @@
3 ## Overview
4
5 Beginning with version 2.7, Fossil can send email messages to
6 subscribers to alert them to changes in the repository:
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
@@ -541,26 +541,26 @@
541 control the email alert system, some of which have not been mentioned so
542 far:
543
544 Commands:
545
546 * The [`alerts`](/help?cmd=alerts) command
547 * The [`test-alert`](/help?cmd=test-alert) command
548 * The [`test-add-alerts`](/help?cmd=test-add-alerts) command
549
550 Web pages available to users and subscribers:
551
552 * The [`/subscribe`](/help?cmd=/subscribe) page
553 * The [`/alerts`](/help?cmd=/alerts) page
554 * The [`/unsubscribe`](/help?cmd=/unsubscribe) page
555 * The [`/renew`](/help?cmd=/renew) page
556 * The [`/contact_admin`](/help?cmd=/contact_admin) page
557
558 Administrator-only web pages:
559
560 * The [`/setup_notification`](/help?cmd=/setup_notification) page
561 * The [`/subscribers`](/help?cmd=/subscribers) page
562
563
564 <a id="design"></a>
565 ## Design of Email Alerts
566
567
--- www/alerts.md
+++ www/alerts.md
@@ -3,11 +3,11 @@
3 ## Overview
4
5 Beginning with version 2.7, Fossil can send email messages to
6 subscribers to alert them to changes in the repository:
7
8 * New [checkins](/help/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
@@ -541,26 +541,26 @@
541 control the email alert system, some of which have not been mentioned so
542 far:
543
544 Commands:
545
546 * The [`alerts`](/help/alerts) command
547 * The [`test-alert`](/help/test-alert) command
548 * The [`test-add-alerts`](/help/test-add-alerts) command
549
550 Web pages available to users and subscribers:
551
552 * The [`/subscribe`](/help/www/subscribe) page
553 * The [`/alerts`](/help/www/alerts) page
554 * The [`/unsubscribe`](/help/www/unsubscribe) page
555 * The [`/renew`](/help/www/renew) page
556 * The [`/contact_admin`](/help/www/contact_admin) page
557
558 Administrator-only web pages:
559
560 * The [`/setup_notification`](/help/www/setup_notification) page
561 * The [`/subscribers`](/help/www/subscribers) page
562
563
564 <a id="design"></a>
565 ## Design of Email Alerts
566
567
+147 -31
--- www/antibot.wiki
+++ www/antibot.wiki
@@ -1,17 +1,34 @@
11
<title>Defense Against Robots</title>
22
3
-A typical Fossil website can have millions of pages, and many of
4
-those pages (for example diffs and annotations and tarballs) can
5
-be expensive to compute.
3
+A typical Fossil website can have billions and billions of pages,
4
+and many of those pages (for example diffs and annotations and tarballs)
5
+can be expensive to compute.
66
If a robot walks a Fossil-generated website,
77
it can present a crippling bandwidth and CPU load.
8
+A "robots.txt" file can help, but in practice, most robots these
9
+days ignore the robots.txt file, so it won't help much.
810
911
A Fossil website is intended to be used
1012
interactively by humans, not walked by robots. This article
1113
describes the techniques used by Fossil to try to welcome human
1214
users while keeping out robots.
15
+
16
+<h2>Defenses Are Enabled By Default</h2>
17
+
18
+In the latest implementations of Fossil, most robot defenses are
19
+enabled by default. You can probably get by with standing up a
20
+public-facing Fossil instance in the default configuration. But
21
+you can also customize the defenses to serve your particular needs.
22
+
23
+<h2>Customizing Anti-Robot Defenses</h2>
24
+
25
+Admin users can configure robot defenses on the
26
+"Robot Defense Settings" page (/setup_robot).
27
+That page is accessible (to Admin users) from the default menu bar
28
+by click on the "Admin" menu choice, then selecting the
29
+"Robot-Defense" link from the list.
1330
1431
<h2>The Hyperlink User Capability</h2>
1532
1633
Every Fossil web session has a "user". For random passers-by on the internet
1734
(and for robots) that user is "nobody". The "anonymous" user is also
@@ -36,11 +53,11 @@
3653
3754
But requiring a login, even an anonymous login, can be annoying.
3855
Fossil provides other techniques for blocking robots which
3956
are less cumbersome to humans.
4057
41
-<h2>Automatic Hyperlinks Based on UserAgent</h2>
58
+<h2>Automatic Hyperlinks Based on UserAgent and Javascript</h2>
4259
4360
Fossil has the ability to selectively enable hyperlinks for users
4461
that lack the <b>Hyperlink</b> capability based on their UserAgent string in the
4562
HTTP request header and on the browsers ability to run Javascript.
4663
@@ -68,21 +85,22 @@
6885
to "play nicely" on the internet and are quite open
6986
about the fact that they are a robot. And so the UserAgent string
7087
provides a good first-guess about whether or not a request originates
7188
from a human or a robot.
7289
73
-In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled
74
-"<b>Enable hyperlinks based on User-Agent and/or Javascript</b>".
75
-If this setting is set to "UserAgent only" or "UserAgent and Javascript",
76
-and if the UserAgent string looks like a human and not a robot, then
90
+The [/help/auto-hyperlink|auto-hyperlink] setting, shown as
91
+"<b>Enable hyperlinks based on User-Agent and/or Javascript</b>" on
92
+the Robot Defense Settings page,
93
+can be set to "UserAgent only" or "UserAgent and Javascript" or "off".
94
+If the UserAgent string looks like a human and not a robot, then
7795
Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability
7896
is omitted from the user permissions. This setting gives humans easy
7997
access to the hyperlinks while preventing robots
8098
from walking the billions of pages on a typical Fossil site.
8199
82
-If the setting is "UserAgent only", then the hyperlinks are simply
83
-enabled and that is all. But if the setting is "UserAgent and Javascript",
100
+If the setting is "UserAgent only" (2), then the hyperlinks are simply
101
+enabled and that is all. But if the setting is "UserAgent and Javascript" (1),
84102
then the hyperlinks are not enabled directly.
85103
Instead, the HTML code that is generated contains anchor tags ("&lt;a&gt;")
86104
with "href=" attributes that point to [/honeypot] rather than the correct
87105
link. JavaScript code is added to the end of the page that goes back and
88106
fills in the correct "href=" attributes of
@@ -92,30 +110,14 @@
92110
UserAgent string. Most robots do not bother to run JavaScript and
93111
so to the robot the empty anchor tag will be useless. But all modern
94112
web browsers implement JavaScript, so hyperlinks will show up
95113
normally for human users.
96114
97
-<h2>Further Defenses</h2>
98
-
99
-Recently (as of this writing, in the spring of 2013) the Fossil server
100
-on the SQLite website ([http://www.sqlite.org/src/]) has been hit repeatedly
101
-by Chinese robots that use forged UserAgent strings to make them look
102
-like normal web browsers and which interpret JavaScript. We do not
103
-believe these attacks to be nefarious since SQLite is public domain
104
-and the attackers could obtain all information they ever wanted to
105
-know about SQLite simply by cloning the repository. Instead, we
106
-believe these "attacks" are coming from "script kiddies". But regardless
107
-of whether or not malice is involved, these attacks do present
108
-an unnecessary load on the server which reduces the responsiveness of
109
-the SQLite website for well-behaved and socially responsible users.
110
-For this reason, additional defenses against
111
-robots have been put in place.
112
-
113
-On the Admin/Robot-Defense page of Fossil, just below the
114
-"<b>Enable hyperlinks using User-Agent and/or Javascript</b>"
115
-setting, there are now two additional sub-settings that can be optionally
116
-enabled to control hyperlinks.
115
+If the [/help/auto-hyperlink|"auto-hyperlink"] setting is (2)
116
+"<b>Enable hyperlinks using User-Agent and/or Javascript</b>",
117
+then there are now two additional sub-settings that control when
118
+hyperlinks are enabled.
117119
118120
The first new sub-setting is a delay (in milliseconds) before setting
119121
the "href=" attributes on anchor tags. The default value for this
120122
delay is 10 milliseconds. The idea here is that a robots will try to
121123
interpret the links on the page immediately, and will not wait for delayed
@@ -130,13 +132,127 @@
130132
131133
See also [./loadmgmt.md|Managing Server Load] for a description
132134
of how expensive pages can be disabled when the server is under heavy
133135
load.
134136
137
+<h2>Do Not Allow Robot Access To Certain Pages</h2>
138
+
139
+The [/help/robot-restrict|robot-restrict setting] is a comma-separated
140
+list of GLOB patterns for pages for which robot access is prohibited.
141
+The default value is:
142
+
143
+<blockquote><pre>
144
+timelineX,diff,annotate,fileage,file,finfo,reports
145
+</pre></blockquote>
146
+
147
+Each entry corresponds to the first path element on the URI for a
148
+Fossil-generated page. If Fossil does not know for certain that the
149
+HTTP request is coming from a human, then any attempt to access one of
150
+these pages brings up a javascript-powered captcha. The user has to
151
+click the accept button the captcha once, and that sets a cookie allowing
152
+the user to continue surfing without interruption for 15 minutes or so
153
+before being presented with another captcha.
154
+
155
+Some path elements have special meanings:
156
+
157
+ * <b>timelineX &rarr;</b>
158
+ This means a subset of /timeline/ pages that are considered
159
+ "expensive". The exact definition of which timeline pages are
160
+ expensive and which are not is still the subject of active
161
+ experimentation and is likely to change by the time you read this
162
+ text. The idea is that anybody (including robots) can see a timeline
163
+ of the most recent changes, but timelines of long-ago change or that
164
+ contain lists of file changes or other harder-to-compute values are
165
+ prohibited.
166
+
167
+ * <b>zip &rarr;</b>
168
+ The special "zip" keyword also matches "/tarball/" and "/sqlar/".
169
+
170
+ * <b>zipX &rarr;</b>
171
+ This is like "zip" in that it restricts access to "/zip/", "/tarball"/
172
+ and "/sqlar/" but with exceptions:<ol type="a">
173
+ <li><p> If the [/help/robot-zip-leaf|robot-zip-leaf] setting is
174
+ true, then tarballs of leaf check-ins are allowed. This permits
175
+ URLs that attempt to download the latest check-in on trunk or
176
+ from a named branch, for example.
177
+ <li><p> If a check-in has a tag that matches the GLOB list in
178
+ [/help/robot-zip-tag|robot-zip-tag], then tarballs of that
179
+ check-in are allowed. This allow check-ins tagged with
180
+ "release" or "allow-robots" (for example) to be downloaded
181
+ without restriction.
182
+ </ol>
183
+ The "zipX" restriction is not in the default robot-restrict setting.
184
+ This is something you might want to add, depending on your needs.
185
+
186
+ * <b>diff &rarr;</b>
187
+ This matches /vdiff/ and /fdiff/ and /vpatch/ and any other page that
188
+ is primarily about showing the difference between two check-ins or two
189
+ file versioons.
190
+
191
+ * <b>annotate &rarr;</b>
192
+ This also matches /blame/ and /praise/.
193
+
194
+Other special keywords may be added in the future.
195
+
196
+The default [/help/robot-restrict|robot-restrict]
197
+setting has been shown in practice to do a good job of keeping
198
+robots from consuming all available CPU and bandwidth while will
199
+still allowing humans access to the full power of the site without
200
+having to be logged in.
201
+
202
+One possible enhancement is to add "zipX" to the
203
+[/help/robot-restrict|robot-restrict] setting,
204
+and enable [help?cmd=robot-zip-leaf|robot-zip-leaf]
205
+and configure [help?cmd=robot-zip-tag|robot-zip-tag].
206
+Do this if you find that robots downloading lots of
207
+obscure tarballs is causing load issues on your site.
208
+
209
+<h2>Anti-robot Exception RegExps</h2>
210
+
211
+The [/help/robot-exception|robot-exception setting] under the name
212
+of <b>Exceptions to anti-robot restrictions</b> is a list of
213
+[/re_rules|regular expressions], one per line, that match
214
+URIs that will bypass the captcha and allow robots full access. The
215
+intent of this setting is to allow automated build scripts
216
+to download specific tarballs of project snapshots.
217
+
218
+The recommended value for this setting allows robots to use URIs of the
219
+following form:
220
+
221
+<blockquote>
222
+<b>https://</b><i>DOMAIN</i><b>/tarball/release/</b><i>HASH</i><b>/</b><i>NAME</i><b>.tar.gz</b>
223
+</blockquote>
224
+
225
+The <i>HASH</i> part of this URL can be any valid
226
+[./checkin_names.wiki|check-in name]. The link works as long as that
227
+check-in is tagged with the "release" symbolic tag. In this way,
228
+robots are permitted to download tarballs (and ZIP archives) of official
229
+releases, but not every intermediate check-in between releases. Humans
230
+who are willing to click the captcha can still download whatever they
231
+want, but robots are blocked by the captcha. This prevents aggressive
232
+robots from downloading tarballs of every historical check-in of your
233
+project, once per day, which many robots these days seem eager to do.
234
+
235
+For example, on the Fossil project itself, this URL will work, even for
236
+robots:
237
+
238
+<blockquote>
239
+https://fossil-scm.org/home/tarball/release/version-2.27/fossil-scm.tar.gz
240
+</blockquote>
241
+
242
+But the next URL will not work for robots because check-in 3bbd18a284c8bd6a
243
+is not tagged as a "release":
244
+
245
+<blockquote>
246
+https://fossil-scm.org/home/tarball/release/3bbd18a284c8bd6a/fossil-scm.tar.gz
247
+</blockquote>
248
+
249
+The second URL will work for humans, just not robots.
250
+
135251
<h2>The Ongoing Struggle</h2>
136252
137
-Fossil currently does a very good job of providing easy access to humans
253
+Fossil currently does a good job of providing easy access to humans
138254
while keeping out troublesome robots. However, robots
139255
continue to grow more sophisticated, requiring ever more advanced
140256
defenses. This "arms race" is unlikely to ever end. The developers of
141257
Fossil will continue to try improve the robot defenses of Fossil so
142258
check back from time to time for the latest releases and updates.
143259
--- www/antibot.wiki
+++ www/antibot.wiki
@@ -1,17 +1,34 @@
1 <title>Defense Against Robots</title>
2
3 A typical Fossil website can have millions of pages, and many of
4 those pages (for example diffs and annotations and tarballs) can
5 be expensive to compute.
6 If a robot walks a Fossil-generated website,
7 it can present a crippling bandwidth and CPU load.
 
 
8
9 A Fossil website is intended to be used
10 interactively by humans, not walked by robots. This article
11 describes the techniques used by Fossil to try to welcome human
12 users while keeping out robots.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
14 <h2>The Hyperlink User Capability</h2>
15
16 Every Fossil web session has a "user". For random passers-by on the internet
17 (and for robots) that user is "nobody". The "anonymous" user is also
@@ -36,11 +53,11 @@
36
37 But requiring a login, even an anonymous login, can be annoying.
38 Fossil provides other techniques for blocking robots which
39 are less cumbersome to humans.
40
41 <h2>Automatic Hyperlinks Based on UserAgent</h2>
42
43 Fossil has the ability to selectively enable hyperlinks for users
44 that lack the <b>Hyperlink</b> capability based on their UserAgent string in the
45 HTTP request header and on the browsers ability to run Javascript.
46
@@ -68,21 +85,22 @@
68 to "play nicely" on the internet and are quite open
69 about the fact that they are a robot. And so the UserAgent string
70 provides a good first-guess about whether or not a request originates
71 from a human or a robot.
72
73 In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled
74 "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>".
75 If this setting is set to "UserAgent only" or "UserAgent and Javascript",
76 and if the UserAgent string looks like a human and not a robot, then
 
77 Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability
78 is omitted from the user permissions. This setting gives humans easy
79 access to the hyperlinks while preventing robots
80 from walking the billions of pages on a typical Fossil site.
81
82 If the setting is "UserAgent only", then the hyperlinks are simply
83 enabled and that is all. But if the setting is "UserAgent and Javascript",
84 then the hyperlinks are not enabled directly.
85 Instead, the HTML code that is generated contains anchor tags ("&lt;a&gt;")
86 with "href=" attributes that point to [/honeypot] rather than the correct
87 link. JavaScript code is added to the end of the page that goes back and
88 fills in the correct "href=" attributes of
@@ -92,30 +110,14 @@
92 UserAgent string. Most robots do not bother to run JavaScript and
93 so to the robot the empty anchor tag will be useless. But all modern
94 web browsers implement JavaScript, so hyperlinks will show up
95 normally for human users.
96
97 <h2>Further Defenses</h2>
98
99 Recently (as of this writing, in the spring of 2013) the Fossil server
100 on the SQLite website ([http://www.sqlite.org/src/]) has been hit repeatedly
101 by Chinese robots that use forged UserAgent strings to make them look
102 like normal web browsers and which interpret JavaScript. We do not
103 believe these attacks to be nefarious since SQLite is public domain
104 and the attackers could obtain all information they ever wanted to
105 know about SQLite simply by cloning the repository. Instead, we
106 believe these "attacks" are coming from "script kiddies". But regardless
107 of whether or not malice is involved, these attacks do present
108 an unnecessary load on the server which reduces the responsiveness of
109 the SQLite website for well-behaved and socially responsible users.
110 For this reason, additional defenses against
111 robots have been put in place.
112
113 On the Admin/Robot-Defense page of Fossil, just below the
114 "<b>Enable hyperlinks using User-Agent and/or Javascript</b>"
115 setting, there are now two additional sub-settings that can be optionally
116 enabled to control hyperlinks.
117
118 The first new sub-setting is a delay (in milliseconds) before setting
119 the "href=" attributes on anchor tags. The default value for this
120 delay is 10 milliseconds. The idea here is that a robots will try to
121 interpret the links on the page immediately, and will not wait for delayed
@@ -130,13 +132,127 @@
130
131 See also [./loadmgmt.md|Managing Server Load] for a description
132 of how expensive pages can be disabled when the server is under heavy
133 load.
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135 <h2>The Ongoing Struggle</h2>
136
137 Fossil currently does a very good job of providing easy access to humans
138 while keeping out troublesome robots. However, robots
139 continue to grow more sophisticated, requiring ever more advanced
140 defenses. This "arms race" is unlikely to ever end. The developers of
141 Fossil will continue to try improve the robot defenses of Fossil so
142 check back from time to time for the latest releases and updates.
143
--- www/antibot.wiki
+++ www/antibot.wiki
@@ -1,17 +1,34 @@
1 <title>Defense Against Robots</title>
2
3 A typical Fossil website can have billions and billions of pages,
4 and many of those pages (for example diffs and annotations and tarballs)
5 can be expensive to compute.
6 If a robot walks a Fossil-generated website,
7 it can present a crippling bandwidth and CPU load.
8 A "robots.txt" file can help, but in practice, most robots these
9 days ignore the robots.txt file, so it won't help much.
10
11 A Fossil website is intended to be used
12 interactively by humans, not walked by robots. This article
13 describes the techniques used by Fossil to try to welcome human
14 users while keeping out robots.
15
16 <h2>Defenses Are Enabled By Default</h2>
17
18 In the latest implementations of Fossil, most robot defenses are
19 enabled by default. You can probably get by with standing up a
20 public-facing Fossil instance in the default configuration. But
21 you can also customize the defenses to serve your particular needs.
22
23 <h2>Customizing Anti-Robot Defenses</h2>
24
25 Admin users can configure robot defenses on the
26 "Robot Defense Settings" page (/setup_robot).
27 That page is accessible (to Admin users) from the default menu bar
28 by click on the "Admin" menu choice, then selecting the
29 "Robot-Defense" link from the list.
30
31 <h2>The Hyperlink User Capability</h2>
32
33 Every Fossil web session has a "user". For random passers-by on the internet
34 (and for robots) that user is "nobody". The "anonymous" user is also
@@ -36,11 +53,11 @@
53
54 But requiring a login, even an anonymous login, can be annoying.
55 Fossil provides other techniques for blocking robots which
56 are less cumbersome to humans.
57
58 <h2>Automatic Hyperlinks Based on UserAgent and Javascript</h2>
59
60 Fossil has the ability to selectively enable hyperlinks for users
61 that lack the <b>Hyperlink</b> capability based on their UserAgent string in the
62 HTTP request header and on the browsers ability to run Javascript.
63
@@ -68,21 +85,22 @@
85 to "play nicely" on the internet and are quite open
86 about the fact that they are a robot. And so the UserAgent string
87 provides a good first-guess about whether or not a request originates
88 from a human or a robot.
89
90 The [/help/auto-hyperlink|auto-hyperlink] setting, shown as
91 "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>" on
92 the Robot Defense Settings page,
93 can be set to "UserAgent only" or "UserAgent and Javascript" or "off".
94 If the UserAgent string looks like a human and not a robot, then
95 Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability
96 is omitted from the user permissions. This setting gives humans easy
97 access to the hyperlinks while preventing robots
98 from walking the billions of pages on a typical Fossil site.
99
100 If the setting is "UserAgent only" (2), then the hyperlinks are simply
101 enabled and that is all. But if the setting is "UserAgent and Javascript" (1),
102 then the hyperlinks are not enabled directly.
103 Instead, the HTML code that is generated contains anchor tags ("&lt;a&gt;")
104 with "href=" attributes that point to [/honeypot] rather than the correct
105 link. JavaScript code is added to the end of the page that goes back and
106 fills in the correct "href=" attributes of
@@ -92,30 +110,14 @@
110 UserAgent string. Most robots do not bother to run JavaScript and
111 so to the robot the empty anchor tag will be useless. But all modern
112 web browsers implement JavaScript, so hyperlinks will show up
113 normally for human users.
114
115 If the [/help/auto-hyperlink|"auto-hyperlink"] setting is (2)
116 "<b>Enable hyperlinks using User-Agent and/or Javascript</b>",
117 then there are now two additional sub-settings that control when
118 hyperlinks are enabled.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
120 The first new sub-setting is a delay (in milliseconds) before setting
121 the "href=" attributes on anchor tags. The default value for this
122 delay is 10 milliseconds. The idea here is that a robots will try to
123 interpret the links on the page immediately, and will not wait for delayed
@@ -130,13 +132,127 @@
132
133 See also [./loadmgmt.md|Managing Server Load] for a description
134 of how expensive pages can be disabled when the server is under heavy
135 load.
136
137 <h2>Do Not Allow Robot Access To Certain Pages</h2>
138
139 The [/help/robot-restrict|robot-restrict setting] is a comma-separated
140 list of GLOB patterns for pages for which robot access is prohibited.
141 The default value is:
142
143 <blockquote><pre>
144 timelineX,diff,annotate,fileage,file,finfo,reports
145 </pre></blockquote>
146
147 Each entry corresponds to the first path element on the URI for a
148 Fossil-generated page. If Fossil does not know for certain that the
149 HTTP request is coming from a human, then any attempt to access one of
150 these pages brings up a javascript-powered captcha. The user has to
151 click the accept button the captcha once, and that sets a cookie allowing
152 the user to continue surfing without interruption for 15 minutes or so
153 before being presented with another captcha.
154
155 Some path elements have special meanings:
156
157 * <b>timelineX &rarr;</b>
158 This means a subset of /timeline/ pages that are considered
159 "expensive". The exact definition of which timeline pages are
160 expensive and which are not is still the subject of active
161 experimentation and is likely to change by the time you read this
162 text. The idea is that anybody (including robots) can see a timeline
163 of the most recent changes, but timelines of long-ago change or that
164 contain lists of file changes or other harder-to-compute values are
165 prohibited.
166
167 * <b>zip &rarr;</b>
168 The special "zip" keyword also matches "/tarball/" and "/sqlar/".
169
170 * <b>zipX &rarr;</b>
171 This is like "zip" in that it restricts access to "/zip/", "/tarball"/
172 and "/sqlar/" but with exceptions:<ol type="a">
173 <li><p> If the [/help/robot-zip-leaf|robot-zip-leaf] setting is
174 true, then tarballs of leaf check-ins are allowed. This permits
175 URLs that attempt to download the latest check-in on trunk or
176 from a named branch, for example.
177 <li><p> If a check-in has a tag that matches the GLOB list in
178 [/help/robot-zip-tag|robot-zip-tag], then tarballs of that
179 check-in are allowed. This allow check-ins tagged with
180 "release" or "allow-robots" (for example) to be downloaded
181 without restriction.
182 </ol>
183 The "zipX" restriction is not in the default robot-restrict setting.
184 This is something you might want to add, depending on your needs.
185
186 * <b>diff &rarr;</b>
187 This matches /vdiff/ and /fdiff/ and /vpatch/ and any other page that
188 is primarily about showing the difference between two check-ins or two
189 file versioons.
190
191 * <b>annotate &rarr;</b>
192 This also matches /blame/ and /praise/.
193
194 Other special keywords may be added in the future.
195
196 The default [/help/robot-restrict|robot-restrict]
197 setting has been shown in practice to do a good job of keeping
198 robots from consuming all available CPU and bandwidth while will
199 still allowing humans access to the full power of the site without
200 having to be logged in.
201
202 One possible enhancement is to add "zipX" to the
203 [/help/robot-restrict|robot-restrict] setting,
204 and enable [help?cmd=robot-zip-leaf|robot-zip-leaf]
205 and configure [help?cmd=robot-zip-tag|robot-zip-tag].
206 Do this if you find that robots downloading lots of
207 obscure tarballs is causing load issues on your site.
208
209 <h2>Anti-robot Exception RegExps</h2>
210
211 The [/help/robot-exception|robot-exception setting] under the name
212 of <b>Exceptions to anti-robot restrictions</b> is a list of
213 [/re_rules|regular expressions], one per line, that match
214 URIs that will bypass the captcha and allow robots full access. The
215 intent of this setting is to allow automated build scripts
216 to download specific tarballs of project snapshots.
217
218 The recommended value for this setting allows robots to use URIs of the
219 following form:
220
221 <blockquote>
222 <b>https://</b><i>DOMAIN</i><b>/tarball/release/</b><i>HASH</i><b>/</b><i>NAME</i><b>.tar.gz</b>
223 </blockquote>
224
225 The <i>HASH</i> part of this URL can be any valid
226 [./checkin_names.wiki|check-in name]. The link works as long as that
227 check-in is tagged with the "release" symbolic tag. In this way,
228 robots are permitted to download tarballs (and ZIP archives) of official
229 releases, but not every intermediate check-in between releases. Humans
230 who are willing to click the captcha can still download whatever they
231 want, but robots are blocked by the captcha. This prevents aggressive
232 robots from downloading tarballs of every historical check-in of your
233 project, once per day, which many robots these days seem eager to do.
234
235 For example, on the Fossil project itself, this URL will work, even for
236 robots:
237
238 <blockquote>
239 https://fossil-scm.org/home/tarball/release/version-2.27/fossil-scm.tar.gz
240 </blockquote>
241
242 But the next URL will not work for robots because check-in 3bbd18a284c8bd6a
243 is not tagged as a "release":
244
245 <blockquote>
246 https://fossil-scm.org/home/tarball/release/3bbd18a284c8bd6a/fossil-scm.tar.gz
247 </blockquote>
248
249 The second URL will work for humans, just not robots.
250
251 <h2>The Ongoing Struggle</h2>
252
253 Fossil currently does a good job of providing easy access to humans
254 while keeping out troublesome robots. However, robots
255 continue to grow more sophisticated, requiring ever more advanced
256 defenses. This "arms race" is unlikely to ever end. The developers of
257 Fossil will continue to try improve the robot defenses of Fossil so
258 check back from time to time for the latest releases and updates.
259
--- www/backoffice.md
+++ www/backoffice.md
@@ -34,15 +34,15 @@
3434
process is already assigned to do the work, then a new backoffice process
3535
is started to do the work.
3636
3737
This happens for every webpage, regardless of how that webpage is launched,
3838
and regardless of the purpose of the webpage. This also happens on the
39
-server for "[fossil sync](/help?cmd=sync)" and
40
-[fossil clone](/help?cmd=clone)" commands which are implemented as
39
+server for "[fossil sync](/help/sync)" and
40
+[fossil clone](/help/clone)" commands which are implemented as
4141
web requests - albeit requests that the human user never sees.
4242
Web requests can arrive at the Fossil server via direct TCP/IP (for example
43
-when Fossil is started using commands like "[fossil server](/help?cmd=server)")
43
+when Fossil is started using commands like "[fossil server](/help/server)")
4444
or via [CGI](./server/any/cgi.md) or
4545
[SCGI](./server/any/scgi.md) or via SSH.
4646
A backoffice process might be started regardless of the origin of the
4747
request.
4848
@@ -100,11 +100,11 @@
100100
[Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps
101101
fix the problem.) For now, the backoffice must be run manually
102102
on OpenBSD systems.
103103
104104
To set up fully-manual backoffice, first disable the automatic backoffice
105
-using the "[backoffice-disable](/help?cmd=backoffice-disable)" setting.
105
+using the "[backoffice-disable](/help/backoffice-disable)" setting.
106106
107107
fossil setting backoffice-disable on
108108
109109
Then arrange to invoke the backoffice separately using a command
110110
like this:
@@ -111,11 +111,11 @@
111111
112112
fossil backoffice --poll 30 _REPOSITORY-LIST_
113113
114114
Multiple repositories can be named. This one command will handle
115115
launching the backoffice for all of them. There are additional useful
116
-command-line options. See the "[fossil backoffice](/help?cmd=backoffice)"
116
+command-line options. See the "[fossil backoffice](/help/backoffice)"
117117
documentation for details.
118118
119119
The backoffice processes run manually using the "fossil backoffice"
120120
command do not normally use a lease. That means that if you run the
121121
"fossil backoffice" command with --poll and you forget to disable
122122
--- www/backoffice.md
+++ www/backoffice.md
@@ -34,15 +34,15 @@
34 process is already assigned to do the work, then a new backoffice process
35 is started to do the work.
36
37 This happens for every webpage, regardless of how that webpage is launched,
38 and regardless of the purpose of the webpage. This also happens on the
39 server for "[fossil sync](/help?cmd=sync)" and
40 [fossil clone](/help?cmd=clone)" commands which are implemented as
41 web requests - albeit requests that the human user never sees.
42 Web requests can arrive at the Fossil server via direct TCP/IP (for example
43 when Fossil is started using commands like "[fossil server](/help?cmd=server)")
44 or via [CGI](./server/any/cgi.md) or
45 [SCGI](./server/any/scgi.md) or via SSH.
46 A backoffice process might be started regardless of the origin of the
47 request.
48
@@ -100,11 +100,11 @@
100 [Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps
101 fix the problem.) For now, the backoffice must be run manually
102 on OpenBSD systems.
103
104 To set up fully-manual backoffice, first disable the automatic backoffice
105 using the "[backoffice-disable](/help?cmd=backoffice-disable)" setting.
106
107 fossil setting backoffice-disable on
108
109 Then arrange to invoke the backoffice separately using a command
110 like this:
@@ -111,11 +111,11 @@
111
112 fossil backoffice --poll 30 _REPOSITORY-LIST_
113
114 Multiple repositories can be named. This one command will handle
115 launching the backoffice for all of them. There are additional useful
116 command-line options. See the "[fossil backoffice](/help?cmd=backoffice)"
117 documentation for details.
118
119 The backoffice processes run manually using the "fossil backoffice"
120 command do not normally use a lease. That means that if you run the
121 "fossil backoffice" command with --poll and you forget to disable
122
--- www/backoffice.md
+++ www/backoffice.md
@@ -34,15 +34,15 @@
34 process is already assigned to do the work, then a new backoffice process
35 is started to do the work.
36
37 This happens for every webpage, regardless of how that webpage is launched,
38 and regardless of the purpose of the webpage. This also happens on the
39 server for "[fossil sync](/help/sync)" and
40 [fossil clone](/help/clone)" commands which are implemented as
41 web requests - albeit requests that the human user never sees.
42 Web requests can arrive at the Fossil server via direct TCP/IP (for example
43 when Fossil is started using commands like "[fossil server](/help/server)")
44 or via [CGI](./server/any/cgi.md) or
45 [SCGI](./server/any/scgi.md) or via SSH.
46 A backoffice process might be started regardless of the origin of the
47 request.
48
@@ -100,11 +100,11 @@
100 [Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps
101 fix the problem.) For now, the backoffice must be run manually
102 on OpenBSD systems.
103
104 To set up fully-manual backoffice, first disable the automatic backoffice
105 using the "[backoffice-disable](/help/backoffice-disable)" setting.
106
107 fossil setting backoffice-disable on
108
109 Then arrange to invoke the backoffice separately using a command
110 like this:
@@ -111,11 +111,11 @@
111
112 fossil backoffice --poll 30 _REPOSITORY-LIST_
113
114 Multiple repositories can be named. This one command will handle
115 launching the backoffice for all of them. There are additional useful
116 command-line options. See the "[fossil backoffice](/help/backoffice)"
117 documentation for details.
118
119 The backoffice processes run manually using the "fossil backoffice"
120 command do not normally use a lease. That means that if you run the
121 "fossil backoffice" command with --poll and you forget to disable
122
+2 -2
--- www/backup.md
+++ www/backup.md
@@ -68,11 +68,11 @@
6868
#### <a id="other-cfg"></a> Others
6969
7070
A repo’s URL aliases, [interwiki configuration](./interwiki.md), and
7171
[ticket customizations](./custom_tcket.wiki) also do not normally sync.
7272
73
-[cfg]: /help?cmd=configuration
73
+[cfg]: /help/configuration
7474
7575
7676
7777
## <a id="private"></a> Private Branches
7878
@@ -289,11 +289,11 @@
289289
find them in a newly-created repo DB. We get around this by passing
290290
the `--no-repository` flag, which suppresses this behavior. Doing it
291291
this way saves you from needing to go and build a matching version of
292292
`sqlite3` just to restore the backup.
293293
294
-[bu]: /help?cmd=backup
294
+[bu]: /help/backup
295295
[grcp]: https://www.grc.com/passwords.htm
296296
[hb]: https://brew.sh
297297
[hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean
298298
[lz4]: https://lz4.github.io/lz4/
299299
[pbr]: ./private.wiki
300300
--- www/backup.md
+++ www/backup.md
@@ -68,11 +68,11 @@
68 #### <a id="other-cfg"></a> Others
69
70 A repo’s URL aliases, [interwiki configuration](./interwiki.md), and
71 [ticket customizations](./custom_tcket.wiki) also do not normally sync.
72
73 [cfg]: /help?cmd=configuration
74
75
76
77 ## <a id="private"></a> Private Branches
78
@@ -289,11 +289,11 @@
289 find them in a newly-created repo DB. We get around this by passing
290 the `--no-repository` flag, which suppresses this behavior. Doing it
291 this way saves you from needing to go and build a matching version of
292 `sqlite3` just to restore the backup.
293
294 [bu]: /help?cmd=backup
295 [grcp]: https://www.grc.com/passwords.htm
296 [hb]: https://brew.sh
297 [hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean
298 [lz4]: https://lz4.github.io/lz4/
299 [pbr]: ./private.wiki
300
--- www/backup.md
+++ www/backup.md
@@ -68,11 +68,11 @@
68 #### <a id="other-cfg"></a> Others
69
70 A repo’s URL aliases, [interwiki configuration](./interwiki.md), and
71 [ticket customizations](./custom_tcket.wiki) also do not normally sync.
72
73 [cfg]: /help/configuration
74
75
76
77 ## <a id="private"></a> Private Branches
78
@@ -289,11 +289,11 @@
289 find them in a newly-created repo DB. We get around this by passing
290 the `--no-repository` flag, which suppresses this behavior. Doing it
291 this way saves you from needing to go and build a matching version of
292 `sqlite3` just to restore the backup.
293
294 [bu]: /help/backup
295 [grcp]: https://www.grc.com/passwords.htm
296 [hb]: https://brew.sh
297 [hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean
298 [lz4]: https://lz4.github.io/lz4/
299 [pbr]: ./private.wiki
300
+7 -7
--- www/blame.wiki
+++ www/blame.wiki
@@ -1,15 +1,15 @@
11
<title>The Annotate Algorithm</title>
22
33
<h2>1.0 Introduction</h2>
44
5
-The [/help?cmd=annotate|fossil annotate],
6
-[/help?cmd=blame|fossil blame], and
7
-[/help?cmd=praise|fossil praise] commands, and the
8
-[/help?cmd=/annotate|/annotate],
9
-[/help?cmd=/blame|/blame], and
10
-[/help?cmd=/praise|/praise] web pages are all used to show the most
5
+The [/help/annotate|fossil annotate],
6
+[/help/blame|fossil blame], and
7
+[/help/praise|fossil praise] commands, and the
8
+[/help/www/annotate|/annotate],
9
+[/help/www/blame|/blame], and
10
+[/help/www/praise|/praise] web pages are all used to show the most
1111
recent check-in that modified each line of a particular file.
1212
This article overviews the algorithm used to compute the annotation
1313
for a file in Fossil.
1414
1515
<h2>2.0 Algorithm</h2>
@@ -45,11 +45,11 @@
4545
4646
The time-consuming part of this algorithm is step 6b - computing the
4747
diff from all historical versions of the file to the version of the file
4848
under analysis. For a large file that has many historical changes, this
4949
can take several seconds. For this reason, the default
50
-[/help?cmd=/annotate|/annotate] webpage only shows those lines that were
50
+[/help/www/annotate|/annotate] webpage only shows those lines that were
5151
changed by the 20 most recent modifications to the file. This allows
5252
the loop on step 6 to terminate after only 19 diffs instead of the hundreds
5353
or thousands of diffs that might be required for a frequently modified file.
5454
5555
As currently implemented (as of 2015-12-12) the annotate algorithm does not
5656
--- www/blame.wiki
+++ www/blame.wiki
@@ -1,15 +1,15 @@
1 <title>The Annotate Algorithm</title>
2
3 <h2>1.0 Introduction</h2>
4
5 The [/help?cmd=annotate|fossil annotate],
6 [/help?cmd=blame|fossil blame], and
7 [/help?cmd=praise|fossil praise] commands, and the
8 [/help?cmd=/annotate|/annotate],
9 [/help?cmd=/blame|/blame], and
10 [/help?cmd=/praise|/praise] web pages are all used to show the most
11 recent check-in that modified each line of a particular file.
12 This article overviews the algorithm used to compute the annotation
13 for a file in Fossil.
14
15 <h2>2.0 Algorithm</h2>
@@ -45,11 +45,11 @@
45
46 The time-consuming part of this algorithm is step 6b - computing the
47 diff from all historical versions of the file to the version of the file
48 under analysis. For a large file that has many historical changes, this
49 can take several seconds. For this reason, the default
50 [/help?cmd=/annotate|/annotate] webpage only shows those lines that were
51 changed by the 20 most recent modifications to the file. This allows
52 the loop on step 6 to terminate after only 19 diffs instead of the hundreds
53 or thousands of diffs that might be required for a frequently modified file.
54
55 As currently implemented (as of 2015-12-12) the annotate algorithm does not
56
--- www/blame.wiki
+++ www/blame.wiki
@@ -1,15 +1,15 @@
1 <title>The Annotate Algorithm</title>
2
3 <h2>1.0 Introduction</h2>
4
5 The [/help/annotate|fossil annotate],
6 [/help/blame|fossil blame], and
7 [/help/praise|fossil praise] commands, and the
8 [/help/www/annotate|/annotate],
9 [/help/www/blame|/blame], and
10 [/help/www/praise|/praise] web pages are all used to show the most
11 recent check-in that modified each line of a particular file.
12 This article overviews the algorithm used to compute the annotation
13 for a file in Fossil.
14
15 <h2>2.0 Algorithm</h2>
@@ -45,11 +45,11 @@
45
46 The time-consuming part of this algorithm is step 6b - computing the
47 diff from all historical versions of the file to the version of the file
48 under analysis. For a large file that has many historical changes, this
49 can take several seconds. For this reason, the default
50 [/help/www/annotate|/annotate] webpage only shows those lines that were
51 changed by the 20 most recent modifications to the file. This allows
52 the loop on step 6 to terminate after only 19 diffs instead of the hundreds
53 or thousands of diffs that might be required for a frequently modified file.
54
55 As currently implemented (as of 2015-12-12) the annotate algorithm does not
56
--- www/blockchain.md
+++ www/blockchain.md
@@ -208,11 +208,11 @@
208208
this makes it “not a blockchain” is a subjective matter.
209209
210210
[arh]: ./hooks.md
211211
[bse]: https://www.researchgate.net/publication/311572122_What_is_Blockchain_a_Gentle_Introduction
212212
[caps]: ./caps/
213
-[cs]: /help?cmd=clearsign
213
+[cs]: /help/clearsign
214214
[dboc]: https://en.wikipedia.org/wiki/Debasement
215215
[dsig]: https://en.wikipedia.org/wiki/Digital_signature
216216
[fb]: ./branching.wiki
217217
[GPG]: https://gnupg.org/
218218
[PGP]: https://www.openpgp.org/
@@ -278,11 +278,11 @@
278278
[ctcp]: ./cap-theorem.md#cp
279279
[cap]: https://en.wikipedia.org/wiki/CAP_theorem
280280
[dlt]: https://en.wikipedia.org/wiki/Distributed_ledger
281281
[DVCS]: https://en.wikipedia.org/wiki/Distributed_version_control
282282
[fc]: https://en.wikipedia.org/wiki/Fiat_money
283
-[purge]: /help?cmd=purge
283
+[purge]: /help/purge
284284
[shun]: ./shunning.wiki
285285
286286
287287
<a id="dpc"></a>
288288
## Distributed Partial Consensus
@@ -335,11 +335,11 @@
335335
sending each an HTTP GET `/info` query for the artifact ID, concluding
336336
that the commit is legitimate once you get enough HTTP 200 status codes back. All of this is
337337
hypothetical, because Fossil doesn’t do this today.
338338
339339
[AGI]: https://en.wikipedia.org/wiki/Artificial_general_intelligence
340
-[rcks]: /help?cmd=repo-cksum
340
+[rcks]: /help/repo-cksum
341341
342342
343343
344344
<a id="anon"></a>
345345
## Anonymity
@@ -447,14 +447,14 @@
447447
self-contained, and with a smaller attack surface.
448448
449449
450450
[alert]: ./alerts.md
451451
[capi]: ./caps/ref.html#i
452
-[mrep]: /help?cmd=remote
452
+[mrep]: /help/remote
453453
[scost]: https://en.wikipedia.org/wiki/Software_development_effort_estimation
454
-[scrub]: /help?cmd=scrub
455
-[sreg]: /help?cmd=self-register
454
+[scrub]: /help/scrub
455
+[sreg]: /help/self-register
456456
457457
458458
# Conclusion
459459
460460
This author believes it is technologically indefensible to call Fossil a
461461
--- www/blockchain.md
+++ www/blockchain.md
@@ -208,11 +208,11 @@
208 this makes it “not a blockchain” is a subjective matter.
209
210 [arh]: ./hooks.md
211 [bse]: https://www.researchgate.net/publication/311572122_What_is_Blockchain_a_Gentle_Introduction
212 [caps]: ./caps/
213 [cs]: /help?cmd=clearsign
214 [dboc]: https://en.wikipedia.org/wiki/Debasement
215 [dsig]: https://en.wikipedia.org/wiki/Digital_signature
216 [fb]: ./branching.wiki
217 [GPG]: https://gnupg.org/
218 [PGP]: https://www.openpgp.org/
@@ -278,11 +278,11 @@
278 [ctcp]: ./cap-theorem.md#cp
279 [cap]: https://en.wikipedia.org/wiki/CAP_theorem
280 [dlt]: https://en.wikipedia.org/wiki/Distributed_ledger
281 [DVCS]: https://en.wikipedia.org/wiki/Distributed_version_control
282 [fc]: https://en.wikipedia.org/wiki/Fiat_money
283 [purge]: /help?cmd=purge
284 [shun]: ./shunning.wiki
285
286
287 <a id="dpc"></a>
288 ## Distributed Partial Consensus
@@ -335,11 +335,11 @@
335 sending each an HTTP GET `/info` query for the artifact ID, concluding
336 that the commit is legitimate once you get enough HTTP 200 status codes back. All of this is
337 hypothetical, because Fossil doesn’t do this today.
338
339 [AGI]: https://en.wikipedia.org/wiki/Artificial_general_intelligence
340 [rcks]: /help?cmd=repo-cksum
341
342
343
344 <a id="anon"></a>
345 ## Anonymity
@@ -447,14 +447,14 @@
447 self-contained, and with a smaller attack surface.
448
449
450 [alert]: ./alerts.md
451 [capi]: ./caps/ref.html#i
452 [mrep]: /help?cmd=remote
453 [scost]: https://en.wikipedia.org/wiki/Software_development_effort_estimation
454 [scrub]: /help?cmd=scrub
455 [sreg]: /help?cmd=self-register
456
457
458 # Conclusion
459
460 This author believes it is technologically indefensible to call Fossil a
461
--- www/blockchain.md
+++ www/blockchain.md
@@ -208,11 +208,11 @@
208 this makes it “not a blockchain” is a subjective matter.
209
210 [arh]: ./hooks.md
211 [bse]: https://www.researchgate.net/publication/311572122_What_is_Blockchain_a_Gentle_Introduction
212 [caps]: ./caps/
213 [cs]: /help/clearsign
214 [dboc]: https://en.wikipedia.org/wiki/Debasement
215 [dsig]: https://en.wikipedia.org/wiki/Digital_signature
216 [fb]: ./branching.wiki
217 [GPG]: https://gnupg.org/
218 [PGP]: https://www.openpgp.org/
@@ -278,11 +278,11 @@
278 [ctcp]: ./cap-theorem.md#cp
279 [cap]: https://en.wikipedia.org/wiki/CAP_theorem
280 [dlt]: https://en.wikipedia.org/wiki/Distributed_ledger
281 [DVCS]: https://en.wikipedia.org/wiki/Distributed_version_control
282 [fc]: https://en.wikipedia.org/wiki/Fiat_money
283 [purge]: /help/purge
284 [shun]: ./shunning.wiki
285
286
287 <a id="dpc"></a>
288 ## Distributed Partial Consensus
@@ -335,11 +335,11 @@
335 sending each an HTTP GET `/info` query for the artifact ID, concluding
336 that the commit is legitimate once you get enough HTTP 200 status codes back. All of this is
337 hypothetical, because Fossil doesn’t do this today.
338
339 [AGI]: https://en.wikipedia.org/wiki/Artificial_general_intelligence
340 [rcks]: /help/repo-cksum
341
342
343
344 <a id="anon"></a>
345 ## Anonymity
@@ -447,14 +447,14 @@
447 self-contained, and with a smaller attack surface.
448
449
450 [alert]: ./alerts.md
451 [capi]: ./caps/ref.html#i
452 [mrep]: /help/remote
453 [scost]: https://en.wikipedia.org/wiki/Software_development_effort_estimation
454 [scrub]: /help/scrub
455 [sreg]: /help/self-register
456
457
458 # Conclusion
459
460 This author believes it is technologically indefensible to call Fossil a
461
--- www/branching.wiki
+++ www/branching.wiki
@@ -75,11 +75,11 @@
7575
But perhaps Bob is off-network when he does his commit, so he has no way
7676
of knowing that Alice has already committed her changes. Or, it could
7777
be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob
7878
just doesn't want to merge in Alice's changes before he has saved his
7979
own, so he forces the commit to occur using the "--allow-fork" option to
80
-the <b>[/help?cmd=commit | fossil commit]</b> command. For any of these
80
+the <b>[/help/commit | fossil commit]</b> command. For any of these
8181
reasons, two commits against check-in 2 have occurred, so the DAG now
8282
has two leaves.
8383
8484
In such a condition, a person working with this repository has a
8585
dilemma: which version of the project is the "latest" in the sense of
@@ -111,11 +111,11 @@
111111
easier to justify, since presumably the lone developer is never confused
112112
about why there are two or more leaves on that branch. Further
113113
justifications for intentional forking are [#forking | given below].
114114
115115
Let us return to Figure 2. To resolve such situations before they can
116
-become a real problem, Alice can use the <b>[/help?cmd=merge | fossil
116
+become a real problem, Alice can use the <b>[/help/merge | fossil
117117
merge]</b> command to merge Bob's changes into her local copy of
118118
check-in 3. Without arguments, that command merges all leaves on the
119119
current branch. Alice can then verify that the merge is sensible and if
120120
so, commit the results as check-in 5. This results in a DAG as shown in
121121
Figure 3.
@@ -349,11 +349,11 @@
349349
refusing the check-in simply because it would create a fork.
350350
<br><br>
351351
If you are writing such a tool — e.g. a shell script to make
352352
multiple manipulations on a Fossil repo — it's better to make it
353353
smart enough to detect this condition and cope with it, such as
354
- by making a call to <b>[/help?cmd=update | fossil update]</b>
354
+ by making a call to <b>[/help/update | fossil update]</b>
355355
and checking for a merge conflict. That said, if the alternative is
356356
losing information, you may feel justified in creating forks that an
357357
interactive user must later manually clean up with <b>fossil merge</b>
358358
commands.</p></li>
359359
</ol>
@@ -600,11 +600,11 @@
600600
to that branch with a <b>fossil update $BRANCH</b> command. (There is an
601601
implicit autosync in that command, if the option was enabled at the
602602
time of the update.)</p></li>
603603
604604
<li><p>The same thing, only in a fresh checkout directory with a
605
- <b>[/help?cmd=open | fossil open $REPO $BRANCH]</b> command.</p></li>
605
+ <b>[/help/open | fossil open $REPO $BRANCH]</b> command.</p></li>
606606
607607
<li><p>Alan makes his check-in 3 while Betty has check-in 1 or 2 as
608608
the tip in her local clone, but because she's working with an
609609
autosync'd connection to the same upstream repository as Alan, on
610610
attempting what will become check-in 4, she gets the "would fork"
@@ -751,11 +751,11 @@
751751
is especially useful with utility branches. There are several of these
752752
in the SQLite and Fossil repositories: "broken-build," "declined,"
753753
"mistake," etc. As you might guess from these names, such branch names
754754
are used in renaming the tip of one branch to shunt it off away from the
755755
mainline of that branch due to some human error. (See
756
-<b>[/help?cmd=amend | fossil
756
+<b>[/help/amend | fossil
757757
amend]</b> and the Fossil UI check-in amendment features.) This is a
758758
workaround for Fossil's [./shunning.wiki|normal inability to forget
759759
history]: we usually don't want to actually <i>remove</i> history, but
760760
would like to sometimes set some of it aside under a new label.
761761
762762
--- www/branching.wiki
+++ www/branching.wiki
@@ -75,11 +75,11 @@
75 But perhaps Bob is off-network when he does his commit, so he has no way
76 of knowing that Alice has already committed her changes. Or, it could
77 be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob
78 just doesn't want to merge in Alice's changes before he has saved his
79 own, so he forces the commit to occur using the "--allow-fork" option to
80 the <b>[/help?cmd=commit | fossil commit]</b> command. For any of these
81 reasons, two commits against check-in 2 have occurred, so the DAG now
82 has two leaves.
83
84 In such a condition, a person working with this repository has a
85 dilemma: which version of the project is the "latest" in the sense of
@@ -111,11 +111,11 @@
111 easier to justify, since presumably the lone developer is never confused
112 about why there are two or more leaves on that branch. Further
113 justifications for intentional forking are [#forking | given below].
114
115 Let us return to Figure 2. To resolve such situations before they can
116 become a real problem, Alice can use the <b>[/help?cmd=merge | fossil
117 merge]</b> command to merge Bob's changes into her local copy of
118 check-in 3. Without arguments, that command merges all leaves on the
119 current branch. Alice can then verify that the merge is sensible and if
120 so, commit the results as check-in 5. This results in a DAG as shown in
121 Figure 3.
@@ -349,11 +349,11 @@
349 refusing the check-in simply because it would create a fork.
350 <br><br>
351 If you are writing such a tool — e.g. a shell script to make
352 multiple manipulations on a Fossil repo — it's better to make it
353 smart enough to detect this condition and cope with it, such as
354 by making a call to <b>[/help?cmd=update | fossil update]</b>
355 and checking for a merge conflict. That said, if the alternative is
356 losing information, you may feel justified in creating forks that an
357 interactive user must later manually clean up with <b>fossil merge</b>
358 commands.</p></li>
359 </ol>
@@ -600,11 +600,11 @@
600 to that branch with a <b>fossil update $BRANCH</b> command. (There is an
601 implicit autosync in that command, if the option was enabled at the
602 time of the update.)</p></li>
603
604 <li><p>The same thing, only in a fresh checkout directory with a
605 <b>[/help?cmd=open | fossil open $REPO $BRANCH]</b> command.</p></li>
606
607 <li><p>Alan makes his check-in 3 while Betty has check-in 1 or 2 as
608 the tip in her local clone, but because she's working with an
609 autosync'd connection to the same upstream repository as Alan, on
610 attempting what will become check-in 4, she gets the "would fork"
@@ -751,11 +751,11 @@
751 is especially useful with utility branches. There are several of these
752 in the SQLite and Fossil repositories: "broken-build," "declined,"
753 "mistake," etc. As you might guess from these names, such branch names
754 are used in renaming the tip of one branch to shunt it off away from the
755 mainline of that branch due to some human error. (See
756 <b>[/help?cmd=amend | fossil
757 amend]</b> and the Fossil UI check-in amendment features.) This is a
758 workaround for Fossil's [./shunning.wiki|normal inability to forget
759 history]: we usually don't want to actually <i>remove</i> history, but
760 would like to sometimes set some of it aside under a new label.
761
762
--- www/branching.wiki
+++ www/branching.wiki
@@ -75,11 +75,11 @@
75 But perhaps Bob is off-network when he does his commit, so he has no way
76 of knowing that Alice has already committed her changes. Or, it could
77 be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob
78 just doesn't want to merge in Alice's changes before he has saved his
79 own, so he forces the commit to occur using the "--allow-fork" option to
80 the <b>[/help/commit | fossil commit]</b> command. For any of these
81 reasons, two commits against check-in 2 have occurred, so the DAG now
82 has two leaves.
83
84 In such a condition, a person working with this repository has a
85 dilemma: which version of the project is the "latest" in the sense of
@@ -111,11 +111,11 @@
111 easier to justify, since presumably the lone developer is never confused
112 about why there are two or more leaves on that branch. Further
113 justifications for intentional forking are [#forking | given below].
114
115 Let us return to Figure 2. To resolve such situations before they can
116 become a real problem, Alice can use the <b>[/help/merge | fossil
117 merge]</b> command to merge Bob's changes into her local copy of
118 check-in 3. Without arguments, that command merges all leaves on the
119 current branch. Alice can then verify that the merge is sensible and if
120 so, commit the results as check-in 5. This results in a DAG as shown in
121 Figure 3.
@@ -349,11 +349,11 @@
349 refusing the check-in simply because it would create a fork.
350 <br><br>
351 If you are writing such a tool — e.g. a shell script to make
352 multiple manipulations on a Fossil repo — it's better to make it
353 smart enough to detect this condition and cope with it, such as
354 by making a call to <b>[/help/update | fossil update]</b>
355 and checking for a merge conflict. That said, if the alternative is
356 losing information, you may feel justified in creating forks that an
357 interactive user must later manually clean up with <b>fossil merge</b>
358 commands.</p></li>
359 </ol>
@@ -600,11 +600,11 @@
600 to that branch with a <b>fossil update $BRANCH</b> command. (There is an
601 implicit autosync in that command, if the option was enabled at the
602 time of the update.)</p></li>
603
604 <li><p>The same thing, only in a fresh checkout directory with a
605 <b>[/help/open | fossil open $REPO $BRANCH]</b> command.</p></li>
606
607 <li><p>Alan makes his check-in 3 while Betty has check-in 1 or 2 as
608 the tip in her local clone, but because she's working with an
609 autosync'd connection to the same upstream repository as Alan, on
610 attempting what will become check-in 4, she gets the "would fork"
@@ -751,11 +751,11 @@
751 is especially useful with utility branches. There are several of these
752 in the SQLite and Fossil repositories: "broken-build," "declined,"
753 "mistake," etc. As you might guess from these names, such branch names
754 are used in renaming the tip of one branch to shunt it off away from the
755 mainline of that branch due to some human error. (See
756 <b>[/help/amend | fossil
757 amend]</b> and the Fossil UI check-in amendment features.) This is a
758 workaround for Fossil's [./shunning.wiki|normal inability to forget
759 history]: we usually don't want to actually <i>remove</i> history, but
760 would like to sometimes set some of it aside under a new label.
761
762
+2 -2
--- www/build.wiki
+++ www/build.wiki
@@ -335,11 +335,11 @@
335335
change at any time. It is only known to work on Linux systems and has
336336
been seen to work on x86/64 and ARM.
337337
338338
Fossil has builtin support for processing specific features using
339339
<tt>libfuzzer</tt>. The features which can be tested this way are
340
-found in the help text for the [/help?cmd=test-fuzz|test-fuzz
340
+found in the help text for the [/help/test-fuzz|test-fuzz
341341
command].
342342
343343
Fuzzing requires:
344344
345345
* The clang C compiler.
@@ -510,11 +510,11 @@
510510
"should" (as of this writing) suffice. Any older version may or may not
511511
work.</div>
512512
513513
After that succeeds, we need to run the normal build so that those
514514
generated files can be compiled in to the fossil binary, accessible
515
-via the [/help?cmd=/builtin|/builtin page]:
515
+via the [/help/www/builtin|/builtin page]:
516516
517517
<pre><code>$ make</code></pre>
518518
519519
Before checking in those newly-built files, they need to be tested by
520520
running the [/pikchrshow] page. If that page loads, the compilation
521521
--- www/build.wiki
+++ www/build.wiki
@@ -335,11 +335,11 @@
335 change at any time. It is only known to work on Linux systems and has
336 been seen to work on x86/64 and ARM.
337
338 Fossil has builtin support for processing specific features using
339 <tt>libfuzzer</tt>. The features which can be tested this way are
340 found in the help text for the [/help?cmd=test-fuzz|test-fuzz
341 command].
342
343 Fuzzing requires:
344
345 * The clang C compiler.
@@ -510,11 +510,11 @@
510 "should" (as of this writing) suffice. Any older version may or may not
511 work.</div>
512
513 After that succeeds, we need to run the normal build so that those
514 generated files can be compiled in to the fossil binary, accessible
515 via the [/help?cmd=/builtin|/builtin page]:
516
517 <pre><code>$ make</code></pre>
518
519 Before checking in those newly-built files, they need to be tested by
520 running the [/pikchrshow] page. If that page loads, the compilation
521
--- www/build.wiki
+++ www/build.wiki
@@ -335,11 +335,11 @@
335 change at any time. It is only known to work on Linux systems and has
336 been seen to work on x86/64 and ARM.
337
338 Fossil has builtin support for processing specific features using
339 <tt>libfuzzer</tt>. The features which can be tested this way are
340 found in the help text for the [/help/test-fuzz|test-fuzz
341 command].
342
343 Fuzzing requires:
344
345 * The clang C compiler.
@@ -510,11 +510,11 @@
510 "should" (as of this writing) suffice. Any older version may or may not
511 work.</div>
512
513 After that succeeds, we need to run the normal build so that those
514 generated files can be compiled in to the fossil binary, accessible
515 via the [/help/www/builtin|/builtin page]:
516
517 <pre><code>$ make</code></pre>
518
519 Before checking in those newly-built files, they need to be tested by
520 running the [/pikchrshow] page. If that page loads, the compilation
521
--- www/caps/admin-v-setup.md
+++ www/caps/admin-v-setup.md
@@ -261,13 +261,13 @@
261261
262262
Setup users can do many things that Admin users cannot. They may not
263263
only use all of the Admin UI features, they may also:
264264
265265
* See record IDs (RIDs) on screens that show them
266
-* See the MIME type of attachments on [`/ainfo` pages](/help?cmd=/ainfo)
267
-* See a remote repo’s HTTP [cache status](/help?cmd=/cachestat)
268
- and [pull cache entries](/help?cmd=/cacheget)
266
+* See the MIME type of attachments on [`/ainfo` pages](/help/www/ainfo)
267
+* See a remote repo’s HTTP [cache status](/help/www/cachestat)
268
+ and [pull cache entries](/help/www/cacheget)
269269
* Edit a Setup user’s account!
270270
271271
The “Admin” feature of Fossil UI is so-named because Admin users can use
272272
about half of its functions, but only Setup can use these pages:
273273
@@ -395,11 +395,11 @@
395395
396396
397397
### <a id="y"></a>Write Unversioned
398398
399399
Fossil currently doesn’t distinguish the sub-operations of
400
-[`fossil uv`](/help?cmd=uv); they’re all covered by [**WrUnver**][capy]
400
+[`fossil uv`](/help/uv); they’re all covered by [**WrUnver**][capy]
401401
(“y”) capability. Since some of these operations are unconditionally
402402
destructive due to the nature of unversioned content, and since this
403403
goes against Fossil’s philosophy of immutable history, nobody gets cap
404404
“y” on a Fossil repo by default, not even the Setup or Admin users. A
405405
Setup or Admin user must grant cap “y” to someone — not necessarily
@@ -446,16 +446,16 @@
446446
[capa]: ./ref.html#a
447447
[caps]: ./ref.html#s
448448
[capx]: ./ref.html#x
449449
[capy]: ./ref.html#y
450450
451
-[fcp]: https://fossil-scm.org/home/help?cmd=configuration
451
+[fcp]: https://fossil-scm.org/home/help/configuration
452452
[fdp]: ../fossil-v-git.wiki#devorg
453453
[forum]: https://fossil-scm.org/forum/
454
-[fui]: /help?cmd=ui
454
+[fui]: /help/ui
455455
[lg]: ./login-groups.md
456456
[rs]: https://fossil-scm.org/home/doc/trunk/www/settings.wiki
457457
[sia]: https://fossil-scm.org/home/artifact?ln=1259-1260&name=0fda31b6683c206a
458458
[snoy]: https://fossil-scm.org/forum/forumpost/00e1c4ecff
459459
[th1]: ../th1.md
460460
[tt]: https://en.wikipedia.org/wiki/Tiger_team#Security
461461
[webo]: ./#webonly
462462
--- www/caps/admin-v-setup.md
+++ www/caps/admin-v-setup.md
@@ -261,13 +261,13 @@
261
262 Setup users can do many things that Admin users cannot. They may not
263 only use all of the Admin UI features, they may also:
264
265 * See record IDs (RIDs) on screens that show them
266 * See the MIME type of attachments on [`/ainfo` pages](/help?cmd=/ainfo)
267 * See a remote repo’s HTTP [cache status](/help?cmd=/cachestat)
268 and [pull cache entries](/help?cmd=/cacheget)
269 * Edit a Setup user’s account!
270
271 The “Admin” feature of Fossil UI is so-named because Admin users can use
272 about half of its functions, but only Setup can use these pages:
273
@@ -395,11 +395,11 @@
395
396
397 ### <a id="y"></a>Write Unversioned
398
399 Fossil currently doesn’t distinguish the sub-operations of
400 [`fossil uv`](/help?cmd=uv); they’re all covered by [**WrUnver**][capy]
401 (“y”) capability. Since some of these operations are unconditionally
402 destructive due to the nature of unversioned content, and since this
403 goes against Fossil’s philosophy of immutable history, nobody gets cap
404 “y” on a Fossil repo by default, not even the Setup or Admin users. A
405 Setup or Admin user must grant cap “y” to someone — not necessarily
@@ -446,16 +446,16 @@
446 [capa]: ./ref.html#a
447 [caps]: ./ref.html#s
448 [capx]: ./ref.html#x
449 [capy]: ./ref.html#y
450
451 [fcp]: https://fossil-scm.org/home/help?cmd=configuration
452 [fdp]: ../fossil-v-git.wiki#devorg
453 [forum]: https://fossil-scm.org/forum/
454 [fui]: /help?cmd=ui
455 [lg]: ./login-groups.md
456 [rs]: https://fossil-scm.org/home/doc/trunk/www/settings.wiki
457 [sia]: https://fossil-scm.org/home/artifact?ln=1259-1260&name=0fda31b6683c206a
458 [snoy]: https://fossil-scm.org/forum/forumpost/00e1c4ecff
459 [th1]: ../th1.md
460 [tt]: https://en.wikipedia.org/wiki/Tiger_team#Security
461 [webo]: ./#webonly
462
--- www/caps/admin-v-setup.md
+++ www/caps/admin-v-setup.md
@@ -261,13 +261,13 @@
261
262 Setup users can do many things that Admin users cannot. They may not
263 only use all of the Admin UI features, they may also:
264
265 * See record IDs (RIDs) on screens that show them
266 * See the MIME type of attachments on [`/ainfo` pages](/help/www/ainfo)
267 * See a remote repo’s HTTP [cache status](/help/www/cachestat)
268 and [pull cache entries](/help/www/cacheget)
269 * Edit a Setup user’s account!
270
271 The “Admin” feature of Fossil UI is so-named because Admin users can use
272 about half of its functions, but only Setup can use these pages:
273
@@ -395,11 +395,11 @@
395
396
397 ### <a id="y"></a>Write Unversioned
398
399 Fossil currently doesn’t distinguish the sub-operations of
400 [`fossil uv`](/help/uv); they’re all covered by [**WrUnver**][capy]
401 (“y”) capability. Since some of these operations are unconditionally
402 destructive due to the nature of unversioned content, and since this
403 goes against Fossil’s philosophy of immutable history, nobody gets cap
404 “y” on a Fossil repo by default, not even the Setup or Admin users. A
405 Setup or Admin user must grant cap “y” to someone — not necessarily
@@ -446,16 +446,16 @@
446 [capa]: ./ref.html#a
447 [caps]: ./ref.html#s
448 [capx]: ./ref.html#x
449 [capy]: ./ref.html#y
450
451 [fcp]: https://fossil-scm.org/home/help/configuration
452 [fdp]: ../fossil-v-git.wiki#devorg
453 [forum]: https://fossil-scm.org/forum/
454 [fui]: /help/ui
455 [lg]: ./login-groups.md
456 [rs]: https://fossil-scm.org/home/doc/trunk/www/settings.wiki
457 [sia]: https://fossil-scm.org/home/artifact?ln=1259-1260&name=0fda31b6683c206a
458 [snoy]: https://fossil-scm.org/forum/forumpost/00e1c4ecff
459 [th1]: ../th1.md
460 [tt]: https://en.wikipedia.org/wiki/Tiger_team#Security
461 [webo]: ./#webonly
462
--- www/caps/login-groups.md
+++ www/caps/login-groups.md
@@ -117,11 +117,11 @@
117117
you into both A and B, within the restrictions set out above.
118118
119119
Changes are transitive in the same way, provided you check that “apply
120120
to all” box on the user edit screen.
121121
122
-[lg]: /help?cmd=login-group
122
+[lg]: /help/login-group
123123
[sh]: ../server/any/http-over-ssh.md
124124
[wo]: ./index.md#webonly
125125
126126
-----
127127
128128
--- www/caps/login-groups.md
+++ www/caps/login-groups.md
@@ -117,11 +117,11 @@
117 you into both A and B, within the restrictions set out above.
118
119 Changes are transitive in the same way, provided you check that “apply
120 to all” box on the user edit screen.
121
122 [lg]: /help?cmd=login-group
123 [sh]: ../server/any/http-over-ssh.md
124 [wo]: ./index.md#webonly
125
126 -----
127
128
--- www/caps/login-groups.md
+++ www/caps/login-groups.md
@@ -117,11 +117,11 @@
117 you into both A and B, within the restrictions set out above.
118
119 Changes are transitive in the same way, provided you check that “apply
120 to all” box on the user edit screen.
121
122 [lg]: /help/login-group
123 [sh]: ../server/any/http-over-ssh.md
124 [wo]: ./index.md#webonly
125
126 -----
127
128
--- www/caps/ref.html
+++ www/caps/ref.html
@@ -315,13 +315,13 @@
315315
<tr id="z">
316316
<th>z</th>
317317
<th>Zip</th>
318318
<td>
319319
Pull archives of particular repository versions via <a
320
- href="/help?cmd=/zip"><tt>/zip</tt></a>, <a
321
- href="/help?cmd=/tarball"><tt>/tarball</tt></a>, and <a
322
- href="/help?cmd=/sqlar"><tt>/sqlar</tt></a> URLs. This is an
320
+ href="/help/www/zip"><tt>/zip</tt></a>, <a
321
+ href="/help/www/tarball"><tt>/tarball</tt></a>, and <a
322
+ href="/help/www/sqlar"><tt>/sqlar</tt></a> URLs. This is an
323323
expensive capability to grant, because creating such archives can
324324
put a large load on <a href="../server/">a Fossil server</a> which
325325
you may then need to <a href="../loadmgmt.md">manage</a>.
326326
Mnemonic: <b>z</b>ip file download.
327327
</td>
328328
--- www/caps/ref.html
+++ www/caps/ref.html
@@ -315,13 +315,13 @@
315 <tr id="z">
316 <th>z</th>
317 <th>Zip</th>
318 <td>
319 Pull archives of particular repository versions via <a
320 href="/help?cmd=/zip"><tt>/zip</tt></a>, <a
321 href="/help?cmd=/tarball"><tt>/tarball</tt></a>, and <a
322 href="/help?cmd=/sqlar"><tt>/sqlar</tt></a> URLs. This is an
323 expensive capability to grant, because creating such archives can
324 put a large load on <a href="../server/">a Fossil server</a> which
325 you may then need to <a href="../loadmgmt.md">manage</a>.
326 Mnemonic: <b>z</b>ip file download.
327 </td>
328
--- www/caps/ref.html
+++ www/caps/ref.html
@@ -315,13 +315,13 @@
315 <tr id="z">
316 <th>z</th>
317 <th>Zip</th>
318 <td>
319 Pull archives of particular repository versions via <a
320 href="/help/www/zip"><tt>/zip</tt></a>, <a
321 href="/help/www/tarball"><tt>/tarball</tt></a>, and <a
322 href="/help/www/sqlar"><tt>/sqlar</tt></a> URLs. This is an
323 expensive capability to grant, because creating such archives can
324 put a large load on <a href="../server/">a Fossil server</a> which
325 you may then need to <a href="../loadmgmt.md">manage</a>.
326 Mnemonic: <b>z</b>ip file download.
327 </td>
328
+2 -2
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -72,11 +72,11 @@
7272
and if the PATH_INFO string is empty, then Fossil will show a list
7373
of available Fossil repositories.
7474
7575
The "skin" of the reply is determined by the first
7676
repository in the list that has a non-zero
77
-[/help?cmd=repolist-skin|repolist-skin] setting.
77
+[/help/repolist-skin|repolist-skin] setting.
7878
7979
If no repository has such a non-zero repolist-skin setting, then
8080
the repository list is generic HTML without any decoration, with
8181
the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
8282
environment variable. The variable can be defined in the CGI
@@ -193,11 +193,11 @@
193193
repo name is "*", then an unconditional redirect to URL is taken.
194194
195195
196196
<h2 id="jsmode">jsmode: <i>VALUE</i></h2>
197197
198
-Specifies the delivery mode for JavaScript files. See "[/help?cmd=http |
198
+Specifies the delivery mode for JavaScript files. See "[/help/http |
199199
http --jsmode]" for the allowed values and their meanings.
200200
201201
202202
<h2 id="mainmenu">mainmenu: <i>FILE</i></h2>
203203
204204
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -72,11 +72,11 @@
72 and if the PATH_INFO string is empty, then Fossil will show a list
73 of available Fossil repositories.
74
75 The "skin" of the reply is determined by the first
76 repository in the list that has a non-zero
77 [/help?cmd=repolist-skin|repolist-skin] setting.
78
79 If no repository has such a non-zero repolist-skin setting, then
80 the repository list is generic HTML without any decoration, with
81 the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
82 environment variable. The variable can be defined in the CGI
@@ -193,11 +193,11 @@
193 repo name is "*", then an unconditional redirect to URL is taken.
194
195
196 <h2 id="jsmode">jsmode: <i>VALUE</i></h2>
197
198 Specifies the delivery mode for JavaScript files. See "[/help?cmd=http |
199 http --jsmode]" for the allowed values and their meanings.
200
201
202 <h2 id="mainmenu">mainmenu: <i>FILE</i></h2>
203
204
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -72,11 +72,11 @@
72 and if the PATH_INFO string is empty, then Fossil will show a list
73 of available Fossil repositories.
74
75 The "skin" of the reply is determined by the first
76 repository in the list that has a non-zero
77 [/help/repolist-skin|repolist-skin] setting.
78
79 If no repository has such a non-zero repolist-skin setting, then
80 the repository list is generic HTML without any decoration, with
81 the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
82 environment variable. The variable can be defined in the CGI
@@ -193,11 +193,11 @@
193 repo name is "*", then an unconditional redirect to URL is taken.
194
195
196 <h2 id="jsmode">jsmode: <i>VALUE</i></h2>
197
198 Specifies the delivery mode for JavaScript files. See "[/help/http |
199 http --jsmode]" for the allowed values and their meanings.
200
201
202 <h2 id="mainmenu">mainmenu: <i>FILE</i></h2>
203
204
+409 -345
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,44 +1,108 @@
11
<title>Change Log</title>
22
3
-<h2 id='v2_27'>Changes for version 2.27 (pending)</h2><ol>
4
- <li> Fix a SQL injection on the [/help?cmd=/file|/file page]. Thanks to
3
+<h2 id='v2_28'>Changes for version 2.28 (pending)</h2><ol>
4
+ <li> Improvements to [./antibot.wiki|anti-robot defenses]:<ol type="a">
5
+ <li> The default configuration now allows robots to download any tarball
6
+ or similar, to better support of automated build systems.
7
+ <li> New special tag "zipX" for the [/help/robot-restrict|robot-restrict]
8
+ setting blocks robot access to tarballs, but with exceptions to support
9
+ automated build systems.
10
+ <li> Enhancements to the default value for the
11
+ [/help/robot-restrict|robot-restrict setting].
12
+ </ol>
13
+ <li> A drop-down menu of recent branches is now possible for the submenu, and
14
+ is used in the [/dir?ci=trunk|code browser].
15
+ <li> Easier access to generated tarballs and ZIPs:<ol type="a">
16
+ <li> When in the [/dir?ci=trunk|code browser] at the top-level,
17
+ a new "Download" submenu option is available to take the
18
+ user to a page where he can download a tarball or ZIP archive.
19
+ <li> New [/help/www/download|/download page] is available. When
20
+ configured using the new
21
+ [/help/suggested-downloads|suggested-downloads setting], a
22
+ link to [/download] named "Tarballs and ZIPs" appears in the
23
+ [/sitemap] and thus on the hamburger menu.
24
+ <li> The filenames for tarballs and ZIPs are now standardized to
25
+ include a timestamp and a hash prefix.
26
+ </ol>
27
+ <li> Timeline enhancements:<ol type="a">
28
+ <li> A new "Simple" view is available. This is compromise between "Verbose"
29
+ and "Compact" that shows only the check-in hash rather than the full
30
+ detail section. There is an ellipsis that one can click on to see the
31
+ full detail text.
32
+ <li> The artifact hash in the detail section of each timeline entry is now
33
+ emphasized (controlled by CSS) to make it easier to locate amid all
34
+ the other text and links.
35
+ <li> When clicking on the ellipsis in "Compact" or "Simple" views, the ellipsis
36
+ is replaced by a left arrow ("←") which can be clicked to hide the extra
37
+ detail again.
38
+ <li> New setting [/help/timeline-mark-leaves|timeline-mark-leaves] controls
39
+ whether or not leaf check-ins are marked in the timeline.
40
+ <li> "No-graph" timelines (using the "ng" query parameter) now show
41
+ branch colors and bare check-in circles on the left. The check-in
42
+ circles appear, but no lines connecting them.
43
+ ([/timeline?ng|example]).
44
+ </ol>
45
+ <li> The [/help/timeline|timeline command] is enhanced with the new
46
+ "<tt>-u|--for-user</tt>" option.
47
+ <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
48
+ can be used to fix a checkout after moving its repository file.
49
+</ol>
50
+
51
+<h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
52
+ <li> Close a potential Denial-of-Service attack against any public-facing Fossil
53
+ server involving exponential behavior in Fossil's regexp implementation.
54
+ <li> Fix a SQL injection on the [/help/www/file|/file page]. Thanks to
555
additional defenses built into Fossil, as well as good luck, this injection
656
is not exploitable for either data exfiltration or privilege escalation. The
757
only possible result of invoking the injection is a harmless SQL syntax error.
8
- (The [https://en.wikipedia.org/wiki/Swiss_cheese_model|holes in the Swiss cheese]
9
- did not line up!)
10
- <li> Enhance the chng= query parameter on the [/help?cmd=/timeline|timeline page]
11
- so that it works with other query parameters like p=, d=, from=, and to=.
12
- <li> Always include nodes identify by sel1= and sel2= in the /timeline display.
13
- <li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command.
14
- <li> Require at least an anonymous login to access the /blame page and similar,
15
- to help prevent robots from soaking up excess CPU time on such pages.
58
+ <li> Strengthen robot defenses to help prevent public-facing servers from being
59
+ overwhelmed by the latest generation of AI spiders.
60
+ <ol type="a">
61
+ <li> New javascript captcha used to restrict access by user "nobody" to pages
62
+ listed in the [/help/robot-restrict|robot-restrict setting].
63
+ <li> The [/help/robot-exception|robot-exception setting] is available to allow
64
+ access to pages that match a regular expression. Use this, for example, to
65
+ allow curl scripts and similar to download release tarballs.
66
+ <li> Require at least an anonymous login to access the /blame page and similar.
67
+ </ol>
68
+ <li> [/help/www/timeline|Timeline] enhancements:
69
+ <ol type="a">
70
+ <li> The chng= query parameter on the [/help/www/timeline|timeline page]
71
+ so that it works with other query parameters like p=, d=, from=, and to=.
72
+ <li> Always include nodes identify by sel1= and sel2= in the /timeline display.
73
+ <li> Improved title when p= and d= are different.
74
+ </ol>
75
+ <li> Enable the --editor option on the [/help/amend|fossil amend] command.
1676
<li> When walking the filesystem looking for Fossil repositories, avoid descending
1777
into directories named "/proc".
18
- <ll> Reduce memory requirements for sending authenticated sync protocol
78
+ <li> Reduce memory requirements for sending authenticated sync protocol
1979
messages.
20
- </ol>
80
+ <li> Show numstat-style change statistics in the /info and /ckout pages.
81
+ <li> Add the [/help/stash | stash rename] subcommand.
82
+ <li> Add the "-h" option to the "[/help/ls|ls]" command to display
83
+ file hashes for a specific check-in when in verbose mode.
84
+ </ol>
2185
2286
<h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
23
- <li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
87
+ <li>Enhancements to [/help/diff|fossil diff] and similar:
2488
<ol type="a">
2589
<li> The argument to the --from option can be a directory name, causing
2690
Fossil to use files under that directory as the baseline for the diff.
27
- <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
91
+ <li> For "gdiff", if no [/help/gdiff-command|gdiff-command setting]
2892
is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
2993
are available, or a --by diff if not.
3094
<li> The "Reload" button is added to --tk diffs, to bring the displayed
3195
diff up to date with the latest changes on disk.
3296
<li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
3397
diffs of multiple files.
3498
</ol>
35
- <li>Added the [/help?cmd=/ckout|/ckout web page] to provide information
99
+ <li>Added the [/help/www/ckout|/ckout web page] to provide information
36100
about pending changes in a working check-out
37
- <li>Enhancements to the [/help?cmd=ui|fossil ui] command:
101
+ <li>Enhancements to the [/help/ui|fossil ui] command:
38102
<ol type="a">
39
- <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
103
+ <li> Defaults to using the new [/help/www/ckout|/ckout page] as its
40104
start page. Or, if the new "--from PATH" option is present, the
41105
default start page becomes "/ckout?exbase=PATH".
42106
<li> The new "--extpage FILENAME" option opens the named file as if it
43107
where in a [./serverext.wiki|CGI extension]. Example usage: the
44108
person editing this change log has
@@ -46,25 +110,25 @@
46110
press "Reload" on the web browser to view edits.
47111
<li> Accept both IPv4 and IPv6 connections on all platforms, including
48112
Windows and OpenBSD. This also applies to the "fossil server"
49113
command.
50114
</ol>
51
- <li>Enhancements to [/help?cmd=merge|fossil merge]:
115
+ <li>Enhancements to [/help/merge|fossil merge]:
52116
<ol type="a">
53
- <li> Added the [/help?cmd=merge-info|fossil merge-info] command and
117
+ <li> Added the [/help/merge-info|fossil merge-info] command and
54118
especially the --tk option to that command, to provide analysis
55119
of the most recent merge or update operation.
56120
<li> When a merge conflict occurs, a new section is added to the conflict
57121
text that shows Fossil's suggested resolution to the conflict.
58122
</ol>
59
- <li>Enhancements to [/help?cmd=commit|fossil commit]:
123
+ <li>Enhancements to [/help/commit|fossil commit]:
60124
<ol type="a">
61125
<li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
62126
in the check-in comment, it will alert the developer and give
63127
him or her the opportunity to edit the comment before continuing.
64128
This feature is controllable by the
65
- [/help?cmd=verify-comments|verify-comments setting].
129
+ [/help/verify-comments|verify-comments setting].
66130
<li> The new "--if-changes" option causes the commit to become
67131
a quiet no-op if there are no pending changes.
68132
<li> Added the ability to sign check-ins with SSH keys. Artifacts signed
69133
this way are ignored by all previous fossil versions, as if they
70134
were plain-text file content instead of Fossil artifacts.
@@ -76,13 +140,13 @@
76140
</ol>
77141
<li>Deprecate the --comfmtflags and --comment-format global options and
78142
no longer list them in the built-in help, but keep them working for
79143
backwards compatibility.
80144
Alternative TTY comment formatting can still be specified using the
81
- [/help?cmd=comment-format|comment-format setting], if desired. The
145
+ [/help/comment-format|comment-format setting], if desired. The
82146
default comment format is now called "canonical", not "legacy".
83
- <li>Enhancements to the [/help?cmd=/timeline|/timeline page]:
147
+ <li>Enhancements to the [/help/www/timeline|/timeline page]:
84148
<ol type="a">
85149
<li> Added the "ml=" ("Merge-in List") query parameter that works
86150
like "rl=" ("Related List") but adds "mionly" style related
87151
check-ins instead of the full "rel" style.
88152
<li> For "tl=", "rl=", and "ml=", the order of the branches in the
@@ -111,33 +175,33 @@
111175
in which case the timeline shows those check-ins that are both
112176
ancestors of p= and descendants of d=.
113177
<li> The saturation and intensity of user-specified checkin and branch
114178
background colors are automatically adjusted to keep the colors
115179
compatible with the current skin, unless the
116
- [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.
180
+ [/help/raw-bgcolor|raw-bgcolor setting] is turned on.
117181
</ol>
118
- <li>The [/help?cmd=/docfile|/docfile webpage] was added. It works like
182
+ <li>The [/help/www/docfile|/docfile webpage] was added. It works like
119183
/doc but keeps the title of markdown documents with the document rather
120184
that moving it up to the page title.
121
- <li>Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
185
+ <li>Added the [/help/www/clusterlist|/clusterlist page] for analysis
122186
and debugging
123187
<li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
124188
decoding of the artifact described by NAME.
125
- <li>Improvements to the [/help?cmd=patch|fossil patch] command:
189
+ <li>Improvements to the [/help/patch|fossil patch] command:
126190
<ol type="a">
127191
<li> Fix a bug in "fossil patch create" that causes
128
- [/help?cmd=revert|fossil revert] operations that happened
129
- on individualfiles after a [/help?cmd=merge|fossil merge]
192
+ [/help/revert|fossil revert] operations that happened
193
+ on individualfiles after a [/help/merge|fossil merge]
130194
to be omitted from the patch.
131
- <li> Added the [/help?cmd=patch|patch alias] command for managing
195
+ <li> Added the [/help/patch|patch alias] command for managing
132196
aliases for remote checkout names.
133197
</ol>
134
- <li>Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
198
+ <li>Enhancements to on-line help and the [/help/help|fossil help] command:
135199
<ol type="a">
136200
<li> Add the ability to search the help text, either in the UI
137
- (on the [/help?cmd=/search|/search page]) or from the command-line
138
- (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
201
+ (on the [/help/www/search|/search page]) or from the command-line
202
+ (using the "[/help/search|fossil search -h PATTERN]" command.)
139203
<li> Accepts an optional SUBCOMMAND argument following the
140204
COMMAND argument and only shows results for the specified
141205
subcommand, not the entire command.
142206
<li> The -u (--usage) option shows only the command-line syntax
143207
<li> The -o (--options) option shows only the command-line options
@@ -153,11 +217,11 @@
153217
<li> Link the version field in ticket view to a matching checkin or tag.
154218
<li> Show creation time in report and ticket view.
155219
<li> Show previous comments in edit ticket as reference.
156220
</ol>
157221
<li>Added the "hash" query parameter to the
158
- [/help?cmd=/whatis|/whatis webpage].
222
+ [/help/www/whatis|/whatis webpage].
159223
<li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
160224
which alerts subscribers when an admin creates a new user or
161225
when a user's permissions change.
162226
<li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
163227
the substring "description", then the project description for each repository
@@ -169,44 +233,44 @@
169233
<ol type="a">
170234
<li> TH1 now makes a distinction between
171235
[/doc/trunk/www/th1.md#taint|tainted and untainted string values].
172236
This makes it more difficult to write custom TH1 scripts that
173237
contain XSS or SQL-injection bugs. The
174
- [/help?cmd=vuln-report|vuln-report] setting was added to control
238
+ [/help/vuln-report|vuln-report] setting was added to control
175239
what Fossil does when it encounters a potential TH1
176240
security problem.
177
- <li> The "--th" option was removed from the [/help?cmd=pikchr|fossil pikchr]
241
+ <li> The "--th" option was removed from the [/help/pikchr|fossil pikchr]
178242
command.
179243
<li> The "enable_htmlify" TH1 command was removed.
180244
</ol>
181
- <li>Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
245
+ <li>Make [/help/www/chat|/chat] better-behaved during server outages, reducing
182246
the frequency of reconnection attempts over time and providing feedback
183247
to the user when the connection is down.
184
- <li>The [/help?cmd=/sqlar|/sqlar] page does not work for users who are not logged
248
+ <li>The [/help/www/sqlar|/sqlar] page does not work for users who are not logged
185249
in, nor are links to that page displayed to users who are not logged in. Being
186250
logged in as "anonymous" is sufficient to overcome this restriction, assuming
187251
that "anonymous" can download tarballs and ZIP archives.
188252
<li>Many other minor fixes and additions.
189253
</ol>
190254
191255
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
192256
193
- * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
257
+ * The "[/help/ui|fossil ui /]" command now works even for repositories
194258
that have non-ASCII filenames
195
- * Add the [/help?cmd=tree|fossil tree] command.
259
+ * Add the [/help/tree|fossil tree] command.
196260
* On case-insensitive filesystems, store files using the filesystem's
197261
preferred case rather than the case typed in by the user.
198262
* Change the name "fossil cherry-pick" command to "fossil cherrypick",
199263
which is more familiar to Git users. Retain the legacy name for
200264
compatibility.
201
- * Add new query parameters to the [/help?cmd=/timeline|/timeline page]:
265
+ * Add new query parameters to the [/help/www/timeline|/timeline page]:
202266
d2=, p2=, and dp2=.
203
- * Add options to the [/help?cmd=tag|fossil tag] command that will list tag values.
204
- * Add the -b|--brief option to the [/help?cmd=status|fossil status] command.
205
- * Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page].
206
- * Add history search to the [/help?cmd=/chat|/chat page].
207
- * Add Unix socket support to the [/help?cmd=server|server command].
267
+ * Add options to the [/help/tag|fossil tag] command that will list tag values.
268
+ * Add the -b|--brief option to the [/help/status|fossil status] command.
269
+ * Add ability to upload unversioned files via the [/help/www/uvlist|/uvlist page].
270
+ * Add history search to the [/help/www/chat|/chat page].
271
+ * Add Unix socket support to the [/help/server|server command].
208272
* On Windows, use the root certificates managed by the operating system
209273
(requires OpenSSL 3.2.0 or greater).
210274
* Take into account zero-width and double-width unicode characters when
211275
formatting the command-line timeline.
212276
* Update the built-in SQLite to version 3.47.0. Precompiled binaries are
@@ -243,11 +307,11 @@
243307
</ul>
244308
* If an "ssh:" sync fails in a way that suggests that the fossil executable
245309
could not be found on the remote host, then retry after adding a PATH=
246310
prefix to the command. This helps "ssh:" to "just work" when the server
247311
is a Mac.
248
- * Enhancements to the [/help?cmd=/timeline|/timeline page]:
312
+ * Enhancements to the [/help/www/timeline|/timeline page]:
249313
<ul>
250314
<li> Add the x= query paramater
251315
<li> Add the shortcut tl= and rl= query parameters
252316
<li> Add support for from=,ft= and from=,bt= query parameter combinations
253317
<li> Automatically highlight the endpoints for from=,to= queries.
@@ -257,16 +321,16 @@
257321
* Moved the /museum/repo.fossil file referenced from the Dockerfile from
258322
the ENTRYPOINT to the CMD part to allow use of --repolist mode.
259323
* The [/uvlist] page now shows the hash algorithm used so that
260324
viewers don't have to guess. The hash is shown in a fixed-width
261325
font for a more visually pleasing display.
262
- * If the [/help?cmd=autosync|autosync setting] contains keyword "all",
326
+ * If the [/help/autosync|autosync setting] contains keyword "all",
263327
the automatic sync occurs against all defined remote repositories, not
264328
just the default.
265329
* Markdown formatter: improved handling of indented fenced code blocks
266330
that contain blank lines.
267
- * When doing a "[/help?cmd=add|fossil add]" on a system with case-insensitive
331
+ * When doing a "[/help/add|fossil add]" on a system with case-insensitive
268332
but case-preserving filenames (Mac and Windows) try to use the filename
269333
case as it is known to the filesystem, not the case entered by the
270334
user on the command-line. See
271335
[forum:/forumpost/30d9c0d131610f53|forum thread 30d9c0d131610f53].
272336
* Fix problems with one-click unsubscribe on email notifications.
@@ -280,17 +344,17 @@
280344
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
281345
282346
* Add ability to "close" forum threads, such that unprivileged users
283347
may no longer respond to them. Only administrators can close
284348
threads or respond to them by default, and the
285
- [/help?cmd=forum-close-policy|forum-close-policy setting] can be
349
+ [/help/forum-close-policy|forum-close-policy setting] can be
286350
used to add that capability to moderators.
287
- * Add the [/help?cmd=all|fossil all whatis] command.
288
- * The [/help?cmd=status|fossil status] command and relevant UI pages now
351
+ * Add the [/help/all|fossil all whatis] command.
352
+ * The [/help/status|fossil status] command and relevant UI pages now
289353
correctly report files which were both renamed <b>and</b> edited as such.
290354
* Show default value of settings that have a default in
291
- [/help?cmd=help|fossil help SETTING] output.
355
+ [/help/help|fossil help SETTING] output.
292356
* On timeline graphs, show closed check-ins using an X in the middle of the
293357
node circle or box.
294358
* New options for email notification: Get email only for the first
295359
post in each new thread, and/or posts that are in reply to my posts.
296360
* Fix a regression bug introduced in version 2.22 that caused FTS5 searches
@@ -303,35 +367,35 @@
303367
<li> Better defense against cross-site request forgery (CSRF)
304368
attacks.
305369
<li> Improvements to static analysis of source code (the codecheck1.c
306370
file in the source tree).
307371
</ul>
308
- * Enhance the [/help?cmd=/dir|treeview file listings]
372
+ * Enhance the [/help/www/dir|treeview file listings]
309373
([/dir?type=tree&ci=trunk|example]) by displaying file sizes
310374
and adding the option to sort by file size.
311
- * The [/help?cmd=fts-config|fossil fts-config] command now shows how much
375
+ * The [/help/fts-config|fossil fts-config] command now shows how much
312376
repository space is used by the full-text index.
313377
* Changing a setting to an empty string is now the same as deleting the
314378
setting, in most cases. There are a few exceptions, indicated by the
315379
keep-empty flag on the setting definition.
316
- * The [/help?cmd=branch|fossil branch list] command can now filter branches
380
+ * The [/help/branch|fossil branch list] command can now filter branches
317381
that have/have not been merged into the current branch.
318382
* Improvements to interactions with remote repositories over SSH:
319383
<ul>
320384
<li> Print the text of the SSH command that is run to do remote interaction,
321385
for full disclosure to the operator.
322
- <li> Add a PATH= argument to the [/help?cmd=ui|fossil ui remote:/] and
323
- [/help?cmd=patch|fossil patch push/pull remote:...] commands so that
386
+ <li> Add a PATH= argument to the [/help/ui|fossil ui remote:/] and
387
+ [/help/patch|fossil patch push/pull remote:...] commands so that
324388
they work when the "remote" machine is a Mac and the "fossil"
325389
executable is in the $HOME/bin directory.
326390
</ul>
327391
* Update built-in libraries SQLite, ZLib, Pikchr to their latest versions.
328392
* Documentation enhancements and typo fixes.
329393
330394
331395
<h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2>
332
- * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a">
396
+ * Enhancements to the [/help/www/timeline|/timeline webpage]: <ol type="a">
333397
<li> Add the ft=TAG query parameter which in combination with d=Y
334398
shows all descendants of Y up to TAG
335399
<li> Enhance the s=PATTERN (search) query parameter so that forum post
336400
text is also searched when the "vfx" query parameter is used
337401
<li> Fix the u= (user) query parameter so that it works with a= and b=
@@ -355,56 +419,56 @@
355419
searching in Chinese.
356420
* Comment lines (starting with a '#') are now supported inside
357421
[./settings.wiki#versionable|versioned settings].
358422
* Default permissions for anonymous users in new repositories are
359423
changed to "hz".
360
- * The [/help?cmd=status|fossil status] command now detects when a
424
+ * The [/help/status|fossil status] command now detects when a
361425
file used to be a symlink and has been replaced by a regular file.
362426
(It previously checked for the inverse case only.)
363
- * The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same
427
+ * The [/help/empty-dirs|empty-dirs setting] now reuses the same
364428
parser as the *-glob settings instead of its prior idiosyncratic
365429
parser, allowing quoted whitespace in patterns.
366
- * Enhancements to the [/help?cmd=/reports|/reports webpage]:
430
+ * Enhancements to the [/help/www/reports|/reports webpage]:
367431
<ol type="a">
368432
<li> The by-week, by-month, and by-year options now show an estimated
369433
size of the current week, month, or year as a dashed box.
370434
<li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins".
371435
</ol>
372436
373437
<h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2>
374438
* Users can request a password reset. This feature is disabled by default.
375
- Use the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it.
376
- New web pages [/help?cmd=/resetpw|/resetpw] and
377
- [/help?cmd=/reqpwreset|/reqpwreset] added.
378
- * Add the [/help?cmd=repack|fossil repack] command (together with
379
- [/help?cmd=all|fossil all repack]) as a convenient way to optimize the
439
+ Use the new [/help/self-pw-reset|self-pw-reset property] to enable it.
440
+ New web pages [/help/www/resetpw|/resetpw] and
441
+ [/help/www/reqpwreset|/reqpwreset] added.
442
+ * Add the [/help/repack|fossil repack] command (together with
443
+ [/help/all|fossil all repack]) as a convenient way to optimize the
380444
size of one or all of the repositories on a system.
381445
* Add the ability to put text descriptions on ticket report formats.
382446
* Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
383
- * The [/help?cmd=/chat|/chat page] can now embed fossil-rendered
447
+ * The [/help/www/chat|/chat page] can now embed fossil-rendered
384448
views of wiki/markdown/pikchr file attachments with the caveat that such
385449
embedding happens in an iframe and thus does not inherit styles and such
386450
from the containing browser window.
387
- * The [/help?cmd=all|fossil all remote] subcommand added to "fossil all".
451
+ * The [/help/all|fossil all remote] subcommand added to "fossil all".
388452
* Passwords for remembered remote repositories are now stored as irreversible
389453
hashes rather than obscured clear-text, for improved security.
390454
* Add the "nossl" and "nocompress" options to CGI.
391455
* Update search infrastructure from FTS4 to FTS5.
392
- * Add the [/help?cmd=/deltachain|/deltachain] page for debugging purposes.
456
+ * Add the [/help/www/deltachain|/deltachain] page for debugging purposes.
393457
* Writes to the database are disabled by default if the HTTP request
394458
does not come from the same origin. This enhancement is a defense in depth
395459
measure only; it does not address any known vulnerabilities.
396460
* Improvements to automatic detection and mitigation of attacks from
397461
malicious robots.
398462
399463
<h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
400
- * Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If
464
+ * Added the [/help/chat-timeline-user|chat-timeline-user setting]. If
401465
it is not an empty string, then any changes that would appear on the timeline
402466
are announced in [./chat.md|the chat room].
403467
* The /unsubscribe page now requests confirmation. [./alerts.md|Email notifications]
404468
now contain only an "Unsubscribe" link, and not a link to subscription management.
405
- * Added the "[/help?cmd=branch|fossil branch lsh]" subcommand to list the
469
+ * Added the "[/help/branch|fossil branch lsh]" subcommand to list the
406470
most recently modified branches.
407471
* More elements of the /info page are now inside of an accordion.
408472
* Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all
409473
commands which still used the former name, for consistency.
410474
* Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch"
@@ -434,11 +498,11 @@
434498
accomplished using a Common Table Expression in the underlying SQL.
435499
* Sort tag listings (command line and webpage) by taking numbers into
436500
consideration so as to cater for tags that follow semantic versioning.
437501
* On the wiki listings, omit by default wiki pages that are associated with
438502
check-ins and branches.
439
- * Add the new "[/help?cmd=describe|fossil describe]" command.
503
+ * Add the new "[/help/describe|fossil describe]" command.
440504
* Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support].
441505
See corresponding [../test/markdown-test3.md|test cases],
442506
[/wiki?name=branch/markdown-footnotes#il|known limitations] and
443507
[forum:/forumthread/ee1f1597e46ec07a|discussion].
444508
* Add the new special name "start:BRANCH" to refer to the first check-in of
@@ -450,96 +514,96 @@
450514
operation. Also require explicit "system" proxy setting to use
451515
"http_proxy" environment variable.
452516
* Reimplemented the [/pikchrshow] app to use a WebAssembly build of
453517
pikchr so that it can render pikchrs on the client instead of requiring
454518
a server round-trip.
455
- * Add the [/help?cmd=email-listid|email-listid setting]. If set, it is
519
+ * Add the [/help/email-listid|email-listid setting]. If set, it is
456520
used as the List-ID header for all outbound notification emails.
457
- * Add the "--branch" option to the "[/help?cmd=timeline|timeline]" command
521
+ * Add the "--branch" option to the "[/help/timeline|timeline]" command
458522
to restrict the displayed items to a specific branch.
459
- * Add the "--versions" option to "[/help?cmd=diff|fossil diff]"
523
+ * Add the "--versions" option to "[/help/diff|fossil diff]"
460524
to display details about the compared versions into the patch header.
461525
* Numerous other minor enhancements.
462526
463527
<h2 id='v2_18'>Changes for version 2.18 (2022-02-23)</h2>
464528
* Added support for [./ssl-server.md|SSL/TLS server mode] for commands
465
- like "[/help?cmd=server|fossil server]" and "[/help?cmd=http|fossil http]"
466
- * The new [/help?cmd=cherry-pick|cherry-pick command] is an alias for
467
- [/help?cmd=merge|merge --cherrypick].
468
- * Add new setting "[/help?cmd=large-file-size|large-file-size]". If the size
529
+ like "[/help/server|fossil server]" and "[/help/http|fossil http]"
530
+ * The new [/help/cherry-pick|cherry-pick command] is an alias for
531
+ [/help/merge|merge --cherrypick].
532
+ * Add new setting "[/help/large-file-size|large-file-size]". If the size
469533
of any file in a commit exceeds this size, a warning is issued.
470
- * Query parameter "year=YYYY" is now accepted by [/help?cmd=/timeline|/timeline].
471
- * The [/help?cmd=tar|tar] and [/help?cmd=zip|zip commands] no longer
534
+ * Query parameter "year=YYYY" is now accepted by [/help/www/timeline|/timeline].
535
+ * The [/help/tar|tar] and [/help/zip|zip commands] no longer
472536
sterilize the manifest file.
473537
* Further improvement to diff alignment in cases that involve both
474538
edits and indentation changes.
475539
* [/doc/trunk/www/chat.md|Chat] improvements:<ul>
476
- <li> [/help?cmd=/chat|The /chat page] input options have been reworked
540
+ <li> [/help/www/chat|The /chat page] input options have been reworked
477541
again for better cross-browser portability.
478
- <li> When sending a [/help?cmd=/chat|/chat] message fails, it is no longer
542
+ <li> When sending a [/help/www/chat|/chat] message fails, it is no longer
479543
immediately lost and sending may optionally be retried.
480
- <li> [/help?cmd=/chat|/chat] can now optionally embed attachments of certain
544
+ <li> [/help/www/chat|/chat] can now optionally embed attachments of certain
481545
types directly into message bodies via an iframe.
482
- <li> Add the "--as FILENAME" option to the "[/help?cmd=chat|fossil chat send]"
546
+ <li> Add the "--as FILENAME" option to the "[/help/chat|fossil chat send]"
483547
command.
484
- <li> Added the "[/help?cmd=chat|fossil chat pull]" command, available to
548
+ <li> Added the "[/help/chat|fossil chat pull]" command, available to
485549
administrators only, for backing up the chat conversation.
486550
</ul>
487
- * Promote the test-detach command into the [/help?cmd=detach|detach command].
488
- * For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option,
551
+ * Promote the test-detach command into the [/help/detach|detach command].
552
+ * For "[/help/pull|fossil pull]" with the --from-parent-project option,
489553
if no URL is specified then use the last URL from the most recent prior
490554
"fossil pull --from-parent-project".
491555
* Add options --project-name and --project-desc to the
492
- "[/help?cmd=init|fossil init]" command.
493
- * The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment
556
+ "[/help/init|fossil init]" command.
557
+ * The [/help/www/ext|/ext page] generates the SERVER_SOFTWARE environment
494558
variable for clients.
495559
* Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such
496560
that it includes the query string. This is how most other systems understand
497561
REQUEST_URI.
498
- * Added the --transport-command option to [/help?cmd=sync|fossil sync]
562
+ * Added the --transport-command option to [/help/sync|fossil sync]
499563
and similar.
500564
501565
<h2 id='v2_17'>Changes for version 2.17 (2021-10-09)</h2>
502566
503567
* Major improvements to the "diff" subsystem, including: <ul>
504
- <li> Added new [/help?cmd=diff|formatting options]: --by, -b, --webpage, --json, --tcl.
568
+ <li> Added new [/help/diff|formatting options]: --by, -b, --webpage, --json, --tcl.
505569
<li> Partial-line matching for unified diffs
506570
<li> Better partial-line matching for side-by-side diffs
507571
<li> Buttons on web-based diffs to show more context
508572
<li> Performance improvements
509573
</ul>
510
- * The --branchcolor option on [/help?cmd=commit|fossil commit] and
511
- [/help?cmd=amend|fossil amend] can now take the value "auto" to
574
+ * The --branchcolor option on [/help/commit|fossil commit] and
575
+ [/help/amend|fossil amend] can now take the value "auto" to
512576
force Fossil to use its built-in automatic color choosing algorithm.
513577
* Fossil now [./concepts.wiki#workflow|autosyncs] prior to running
514
- [/help?cmd=open|fossil open].
515
- * Add the [/help?cmd=ticket-default-report|ticket-default-report setting],
578
+ [/help/open|fossil open].
579
+ * Add the [/help/ticket-default-report|ticket-default-report setting],
516580
which if set to the title of a ticket report causes that ticket report
517581
to be displayed below the search box in the /ticket page.
518
- * The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page
582
+ * The "nc" query parameter to the [/help/www/timeline|/timeline] page
519583
causes all graph coloring to be omitted.
520
- * Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]"
584
+ * Improvements and bug fixes to the new "[/help/ui|fossil ui REMOTE]"
521585
feature so that it works better on a wider variety of platforms.
522
- * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for
586
+ * In [/help/www/wikiedit|/wikiedit], show the list of attachments for
523587
the current page and list URLs suitable for pasting them into the page.
524
- * Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
588
+ * Add the --no-http-compression option to [/help/sync|fossil sync]
525589
and similar.
526
- * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
590
+ * Print total payload bytes on a [/help/sync|fossil sync] when using
527591
the --verbose option.
528592
* Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
529
- </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
530
- * The "-p" option to [/help?cmd=branch|fossil branch list] shows only
593
+ </tt>unhide</tt> subcommands to [/help/branch|the branch command].
594
+ * The "-p" option to [/help/branch|fossil branch list] shows only
531595
private branches.
532596
* The [/md_rules|Markdown formatter] now interprets the content of
533597
block HTML markup (such as &lt;table&gt;) in most cases. Only content
534598
of &lt;pre&gt; and &lt;script&gt; is passed through verbatim.
535
- * The [/help?cmd=wiki|wiki list command] no longer lists "deleted"
599
+ * The [/help/wiki|wiki list command] no longer lists "deleted"
536600
pages by default. Use the new <tt>--all</tt> option to include deleted
537601
pages in the output.
538
- * The [/help?cmd=all|fossil all git status] command only shows reports for
602
+ * The [/help/all|fossil all git status] command only shows reports for
539603
the subset of repositories that have a configured Git export.
540
- * The [/help?cmd=/chat|/chat] configuration was reimplemented and
604
+ * The [/help/www/chat|/chat] configuration was reimplemented and
541605
provides new options, including the ability for a repository
542606
administrator to
543607
[./chat.md#notifications|extend the selection of notification sounds]
544608
using unversioned files.
545609
* Chat now uses fossil's full complement of markdown features,
@@ -548,14 +612,14 @@
548612
markdown-processed when they are sent instead of when they
549613
are saved.
550614
* Added a chat message preview mode so messages can be previewed
551615
before being sent. Similarly, added a per-message ability to view
552616
the raw un-parsed message text.
553
- * The hotkey to activate preview mode in [/help?cmd=/wikiedit|/wikiedit],
554
- [/help?cmd=/fileedit|/fileedit], and [/help?cmd=/pikchrshow|/pikchrshow]
617
+ * The hotkey to activate preview mode in [/help/www/wikiedit|/wikiedit],
618
+ [/help/www/fileedit|/fileedit], and [/help/www/pikchrshow|/pikchrshow]
555619
was changed from ctrl-enter to shift-enter in order to align with
556
- [/help?cmd=/chat|/chat]'s new preview feature and related future
620
+ [/help/www/chat|/chat]'s new preview feature and related future
557621
changes.
558622
559623
<h2 id='v2_16'>Changes for Version 2.16 (2021-07-02)</h2>
560624
* <b>Security:</b> Fix the client-side TLS so that it verifies that the
561625
server hostname matches its certificate.
@@ -562,11 +626,11 @@
562626
* The default "ssh" command on Windows is changed to "ssh" instead of the
563627
legacy "plink", as ssh is now generally available on Windows systems.
564628
Installations that still need to use the legacy "plink" can make that
565629
happen by running: '<tt>fossil set ssh-command "plink -ssh" --global</tt>'.
566630
* Added the [./patchcmd.md|fossil patch] command.
567
- * The [/help?cmd=ui|fossil ui] command is enhanced in multiple ways:<ol>
631
+ * The [/help/ui|fossil ui] command is enhanced in multiple ways:<ol>
568632
<li> The REPOSITORY argument can be the name of a check-out directory.
569633
<li> If the REPOSITORY argument is prefixed by "HOST:" or "USER@HOST:"
570634
then the ui is run on the remote machine and tunnelled back to the local
571635
machine using ssh. (The latest version of fossil must be installed on
572636
both the local and the remote for this to work correctly.)
@@ -575,25 +639,25 @@
575639
* The [/brlist|/brlist web page] allows the user to
576640
select multiple branches to be displayed together in a single
577641
timeline.
578642
* The [./forum.wiki|Forum] provides a hyperlink on the author of each
579643
post that goes to a timeline of recent posts by that same author.
580
- * Added the "[/help?cmd=bisect|fossil bisect run]" command for improved
644
+ * Added the "[/help/bisect|fossil bisect run]" command for improved
581645
automation of bisects.
582
- * The [/help?cmd=merge|fossil merge] command now does a better job merging
646
+ * The [/help/merge|fossil merge] command now does a better job merging
583647
branches where files have been renamed between the current branch and the
584648
branch being merged.
585
- * The [/help?cmd=open|fossil open] command allows the repository file
649
+ * The [/help/open|fossil open] command allows the repository file
586650
to be inside the working directory without requiring the --force flag.
587
- * The [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/wikinew|/wikinew]
651
+ * The [/help/www/wikiedit|/wikiedit] and [/help/www/wikinew|/wikinew]
588652
pages now default to markdown format.
589
- * The [/help?cmd=/login|/login] page now links to a user's forum post
653
+ * The [/help/www/login|/login] page now links to a user's forum post
590654
timeline if the repository has forum posts.
591655
* Tags may now be propagated for forum posts, wiki pages, and technotes.
592
- The [/help?cmd=tag|tag command] can now manipulate and list such tags.
656
+ The [/help/tag|tag command] can now manipulate and list such tags.
593657
* [./caps/login-groups.md|Login-Groups] are now shown on the repository
594
- list of the "[/help?cmd=all|fossil all ui]" command.
658
+ list of the "[/help/all|fossil all ui]" command.
595659
* Administrators can configure [./alerts.md|email alerts] to expire
596660
a specific number of days (ex: 365) after the last user contact with
597661
the Fossil server. This prevents alert emails being sent to
598662
abandoned email accounts forever.
599663
* SQL that defines [/tktsetup_tab|database objects for tickets] now
@@ -610,11 +674,11 @@
610674
* <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
611675
the patch is recommended.</b>
612676
* The [./defcsp.md|default CSP] has been relaxed slightly to allow
613677
images to be loaded from any URL. All other resources are still
614678
locked down by default.
615
- * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
679
+ * The built-in skins all use the "[/help/mainmenu|mainmenu]"
616680
setting to determine the content of the main menu.
617681
The ability to edit the
618682
"mainmenu" setting is added on the /Admin/Configuration page.
619683
* The hamburger menu is now available on most of the built-in skins.
620684
* Any built-in skin named "X" can be used instead of the standard
@@ -624,40 +688,40 @@
624688
included. The [/skins] page may be used to select a skin.
625689
* The [/cookies] page now gives the user an opportunity to delete
626690
individual cookies. And the /cookies page is linked from the
627691
/sitemap, so that it appears in hamburger menus.
628692
* The [/sitemap] extensions are now specified by a single new
629
- "[/help?cmd=sitemap-extra|sitemap-extra setting]",
693
+ "[/help/sitemap-extra|sitemap-extra setting]",
630694
rather than a cluster of various
631695
"sitemap-*" settings. The older settings are no longer used.
632696
<b>This change might require minor server configuration
633697
adjustments on servers that use /sitemap extensions.</b>
634698
The /Admin/Configuration page provides the ability to edit
635699
the new "sitemap-extra" setting.
636700
* Added the "--ckout-alias NAME" option to
637
- [/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and
638
- [/help?cmd=http|fossil http]. This option causes Fossil to
701
+ [/help/ui|fossil ui], [/help/server|fossil server], and
702
+ [/help/http|fossil http]. This option causes Fossil to
639703
understand URIs of the form "/doc/NAME/..." as if they were
640
- "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
704
+ "[/help/www/doc|/doc/ckout/...]", to facilitate testing of
641705
[./embeddeddoc.wiki|embedded documentation] changes prior to
642706
check-in.
643707
* For diff web pages, if the diff type (unified versus side-by-side)
644708
is not specified by a query parameter, and if the
645
- "[/help?cmd=preferred-diff-type|preferred-diff-type]"
709
+ "[/help/preferred-diff-type|preferred-diff-type]"
646710
setting is omitted or less than 1, then select the diff type based
647711
on a guess of whether or not the request is coming from a mobile
648712
device. Mobile gets unified and desktop gets side-by-side.
649713
* The various pages which show diffs now have toggles to show/hide
650714
individual diffs.
651
- * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]"
715
+ * Add the "[/help/preferred-diff-type|preferred-diff-type]"
652716
setting to allow an admin to force a default diff type.
653717
* The "pikchr-background" setting is now available in
654718
"detail.txt" skin files, for better control of Pikchr
655719
colors in inverted color schemes.
656720
* Add the <tt>--list</tt> option to the
657
- [/help?cmd=tarball|tarball],
658
- [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
721
+ [/help/tarball|tarball],
722
+ [/help/zip|zip], and [/help/sqlar|sqlar]
659723
commands.
660724
* The javascript used to implement the hamburger menu on the
661725
default built-in skin has been made generic so that it is usable
662726
by a variety of skins, and promoted to an ordinary built-in
663727
javascript file.
@@ -665,24 +729,24 @@
665729
"[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
666730
"[/doc/trunk/www/th1.md#capexpr|capexpr]",
667731
"foreach", "lappend", and "string match"
668732
* The [/help/leaves|leaves command] now shows the branch point
669733
of each leaf.
670
- * The [/help?cmd=add|fossil add] command refuses to add files whose
734
+ * The [/help/add|fossil add] command refuses to add files whose
671735
names are reserved by Windows (ex: "aux") unless the --allow-reserved
672736
option is included. This helps prevent Unix users from accidentally
673737
creating check-ins that are unreadable by Windows users.
674
- * Add the "re=" query parameter to the [/help?cmd=/dir|/dir] webpage,
675
- for symmetry with the [/help?cmd=/tree|/tree] page.
738
+ * Add the "re=" query parameter to the [/help/www/dir|/dir] webpage,
739
+ for symmetry with the [/help/www/tree|/tree] page.
676740
* Update the built-in SQLite to version 3.35.0.
677741
* The ./configure script now has the --print-minimum-sqlite-version option
678742
that prints the minimum SQLite version required by the current version
679743
of Fossil. This might be used by integrators who insist on building
680744
Fossil to link against the system SQLite library rather than the
681745
built-in copy of SQLite, to verify that their system SQLite library
682746
is recent enough.
683
- * Webpage that shows [/help?cmd=/whistory|history of a wiki page]
747
+ * Webpage that shows [/help/www/whistory|history of a wiki page]
684748
gained client-side UI to help with comparison between two arbitrary
685749
versions of a wiki (by the means of anchoring a "baseline" version)
686750
and the ability to squeeze several sequential edits made by the same
687751
user into a single "recycled" row (the latest edit in that sequence).
688752
@@ -702,40 +766,40 @@
702766
version 2.14 and then later downgrade or otherwise use an earlier
703767
version of Fossil, the email notification mechanism may fail
704768
to send out notifications for some events, due to the missing
705769
trigger. If you want to
706770
permanently downgrade an installation, then you should run
707
- "[/help?cmd=rebuild|fossil rebuild]" after the downgrade
771
+ "[/help/rebuild|fossil rebuild]" after the downgrade
708772
to get email notifications working again. If you are not using
709773
email notification, then the schema change will not affect you in
710774
any way.
711775
* <b>Schema Update Notice #2:</b>
712776
This release changes how the descriptions of wiki edits are stored
713777
in the EVENT table, for improved display on timelines. You must
714
- run "[/help?cmd=rebuild|fossil rebuild]" to take advantage of
778
+ run "[/help/rebuild|fossil rebuild]" to take advantage of
715779
this enhancement. Everything will still work without
716780
"fossil rebuild", except you will get goofy descriptions of
717781
wiki updates in the timeline.
718782
* Add support for [./chat.md|Fossil chat].
719
- * The "[/help?cmd=clone|fossil clone]" command is enhanced so that
783
+ * The "[/help/clone|fossil clone]" command is enhanced so that
720784
if the repository filename is omitted, an appropriate name is derived
721785
from the remote URL and the newly cloned repo is opened. This makes
722786
the clone command work more like Git, thus making it easier for
723787
people transitioning from Git.
724
- * Added the --mainbranch option to the [/help?cmd=git|fossil git export]
788
+ * Added the --mainbranch option to the [/help/git|fossil git export]
725789
command.
726790
* Added the --format option to the
727
- "[/help?cmd=timeline|fossil timeline]" command.
791
+ "[/help/timeline|fossil timeline]" command.
728792
* Enhance the --numstat option on the
729
- "[/help?cmd=diff|fossil diff]" command so that it shows a total
793
+ "[/help/diff|fossil diff]" command so that it shows a total
730794
number of lines added and deleted and total number of files
731795
modified.
732
- * Add the "contact" sub-command to [/help?cmd=user|fossil user].
733
- * Added commands "[/help?cmd=all|fossil all git export]" and
734
- "[/help?cmd=all|fossil all git status]".
796
+ * Add the "contact" sub-command to [/help/user|fossil user].
797
+ * Added commands "[/help/all|fossil all git export]" and
798
+ "[/help/all|fossil all git status]".
735799
* Added the "df=CHECKIN" query parameter to the
736
- [/help?cmd=/timeline|/timeline page].
800
+ [/help/www/timeline|/timeline page].
737801
* Improvements to the "[/sitemap]" page. Add subpages
738802
[/sitemap-timeline] and [/sitemap-test].
739803
* Better text position in cylinder objects of Pikchr diagrams.
740804
* New "details.txt" settings available to custom skins to better control
741805
the rendering of Pikchr diagrams:
@@ -757,24 +821,24 @@
757821
* Added support for [./interwiki.md|interwiki links].
758822
* Enable &lt;del&gt; and &lt;ins&gt; markup in wiki.
759823
* Improvements to the Forum threading display.
760824
* Added support for embedding [./pikchr.md|pikchr]
761825
markup in markdown and fossil-wiki content.
762
- * The new "[/help?cmd=pikchr|pikchr]" command can render
826
+ * The new "[/help/pikchr|pikchr]" command can render
763827
pikchr scripts, optionally pre-processed with
764828
[/doc/trunk/www/th1.md|TH1] blocks and variables exactly like
765829
site skins are.
766
- * The new [/help?cmd=/pikchrshow|pikchrshow] page provides an
830
+ * The new [/help/www/pikchrshow|pikchrshow] page provides an
767831
editor and previewer for pikchr markup.
768
- * In [/help?cmd=/wikiedit|/wikiedit] and
769
- [/help?cmd=/fileedit|/fileedit], Ctrl-Enter can now be used
832
+ * In [/help/www/wikiedit|/wikiedit] and
833
+ [/help/www/fileedit|/fileedit], Ctrl-Enter can now be used
770834
initiate a preview and to toggle between the editor and preview
771835
tabs.
772836
* The <tt>/artifact</tt> and <tt>/file</tt> views, when in
773837
line-number mode, now support interactive selection of a range
774838
of lines to hyperlink to.
775
- * Enhance the [/help?cmd=/finfo|/finfo] webpage so that when query
839
+ * Enhance the [/help/www/finfo|/finfo] webpage so that when query
776840
parameters identify both a filename and a checkin, the resulting
777841
graph tracks the identified file across renames.
778842
* The built-in SQLite is updated to an alpha of version 3.34.0, and
779843
the minimum SQLite version is increased to 3.34.0 because the
780844
/finfo change in the previous bullet depends on enhancements to
@@ -783,18 +847,18 @@
783847
* Countless other minor refinements and documentation improvements.
784848
785849
<h2 id='v2_12'>Changes for Version 2.12.1 (2020-08-20)</h2>
786850
787851
* (2.12.1): Fix client-side vulnerabilities discovered by Max Justicz.
788
- * Security fix in the "[/help?cmd=git|fossil git export]" command.
852
+ * Security fix in the "[/help/git|fossil git export]" command.
789853
The same fix is also backported to version 2.10.1 and 2.11.1.
790854
New "safety-net" features were added to prevent similar problems
791855
in the future.
792856
* Enhancements to the graph display for cases when there are
793857
many cherry-pick merges into a single check-in.
794858
[/timeline?f=2d75e87b760c0a9|Example]
795
- * Enhance the [/help?cmd=open|fossil open] command with the new
859
+ * Enhance the [/help/open|fossil open] command with the new
796860
--workdir option and the ability to accept a URL as the repository
797861
name, causing the remote repository to be cloned automatically.
798862
Do not allow "fossil open" to open in a non-empty working directory
799863
unless the --keep option or the new --force option is used.
800864
* Enhance the markdown formatter to more closely follow the
@@ -803,65 +867,65 @@
803867
Underscores in the middle of identifiers (ex: fossil_printf())
804868
no longer need to be escaped.
805869
* The markdown-to-html translator can prevent unsafe HTML
806870
(for example: &lt;script&gt;) on user-contributed pages like forum and
807871
tickets and wiki. The admin can adjust this behavior using
808
- the [/help?cmd=safe-html|safe-html setting] on the Admin/Wiki page.
872
+ the [/help/safe-html|safe-html setting] on the Admin/Wiki page.
809873
The default is to disallow unsafe HTML everywhere.
810874
[https://fossil-scm.org/forum/forumpost/3714e6568f|Example].
811875
* Added the "collapse" and "expand" capability for long forum posts.
812876
[https://fossil-scm.org/forum/forumpost/9297029862|Example]
813
- * The "[/help?cmd=remote-url|fossil remote]" command now has options for
877
+ * The "[/help/remote-url|fossil remote]" command now has options for
814878
specifying multiple persistent remotes with symbolic names. Currently
815879
only one remote can be used at a time, but that might change in the
816880
future.
817881
* Add the "Remember me?" checkbox on the login page. Use a session
818882
cookie for the login if it is not checked.
819
- * Added the experimental "[/help?cmd=hook|fossil hook]" command for
883
+ * Added the experimental "[/help/hook|fossil hook]" command for
820884
managing "hook scripts" that run before checkin or after a push.
821
- * Enhance the [/help?cmd=revert|fossil revert] command so that it
885
+ * Enhance the [/help/revert|fossil revert] command so that it
822886
is able to revert all files beneath a directory.
823
- * Add the [/help?cmd=bisect|fossil bisect skip] command.
824
- * Add the [/help?cmd=backup|fossil backup] command.
825
- * Enhance [/help?cmd=bisect|fossil bisect ui] so that it shows all unchecked
887
+ * Add the [/help/bisect|fossil bisect skip] command.
888
+ * Add the [/help/backup|fossil backup] command.
889
+ * Enhance [/help/bisect|fossil bisect ui] so that it shows all unchecked
826890
check-ins in between the innermost "good" and "bad" check-ins.
827
- * Added the <tt>--reset</tt> flag to the "[/help?cmd=add|fossil add]",
828
- "[/help?cmd=rm|fossil rm]", and
829
- "[/help?cmd=addremove|fossil addremove]" commands.
891
+ * Added the <tt>--reset</tt> flag to the "[/help/add|fossil add]",
892
+ "[/help/rm|fossil rm]", and
893
+ "[/help/addremove|fossil addremove]" commands.
830894
* Added the "<tt>--min</tt> <i>N</i>" and "<tt>--logfile</tt> <i>FILENAME</i>"
831
- flags to the [/help?cmd=backoffice|backoffice] command, as well as other
895
+ flags to the [/help/backoffice|backoffice] command, as well as other
832896
enhancements to make the backoffice command a viable replacement for
833897
automatic backoffice. Other incremental backoffice improvements.
834
- * Added the [/help?cmd=/fileedit|/fileedit page], which allows
898
+ * Added the [/help/www/fileedit|/fileedit page], which allows
835899
editing of text files online. Requires explicit activation by
836900
a setup user.
837901
* Translate built-in help text into HTML for display on web pages.
838
- [/help?cmd=help|Example].
839
- * On the [/help?cmd=/timeline|/timeline] webpage, the combination
902
+ [/help/help|Example].
903
+ * On the [/help/www/timeline|/timeline] webpage, the combination
840904
of query parameters "p=CHECKIN" and "bt=ANCESTOR" draws all
841905
ancestors of CHECKIN going back to ANCESTOR. For example,
842906
[/timeline?p=202006271506&bt=version-2.11] shows all ancestors
843907
of the checkin that occurred on 2020-06-27 15:06 going back to
844908
the 2.11 release.
845909
* Update the built-in SQLite so that the
846
- "[/help?cmd=sql|fossil sql]" command supports new output
910
+ "[/help/sql|fossil sql]" command supports new output
847911
modes ".mode box" and ".mode json".
848912
* Add the "<tt>obscure()</tt>" SQL function to the
849
- "[/help?cmd=sql|fossil sql]" command.
913
+ "[/help/sql|fossil sql]" command.
850914
* Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
851
- the "[/help?cmd=sql|fossil sql]" command, providing access to the
915
+ the "[/help/sql|fossil sql]" command, providing access to the
852916
dispatch table including all help text, and the builtin data files,
853917
respectively.
854918
* [./delta_format.wiki|Delta compression] is now applied to forum edits.
855
- * The [/help?cmd=/wikiedit|wiki editor] has been modernized and is
919
+ * The [/help/www/wikiedit|wiki editor] has been modernized and is
856920
now Ajax-based. The WYSIWYG editing option for Fossil-format wiki
857921
pages was removed. (Please let us know, via the site's Forum menu,
858922
if that removal unduly impacts you.) This also changes the semantics
859923
of the wiki "Sandbox": that pseudo-page may be freely edited but
860
- no longer saved via the UI (the [/help?cmd=wiki|wiki CLI command]
924
+ no longer saved via the UI (the [/help/wiki|wiki CLI command]
861925
can, though).
862
- * The [/help?cmd=allow-symlinks|allow-symlinks setting] no longer
926
+ * The [/help/allow-symlinks|allow-symlinks setting] no longer
863927
syncs. It must be activated individually on any clones which require
864928
symlinks.
865929
* Countless documentation enhancements.
866930
867931
<h2 id='v2_11'>Changes for Version 2.11 (2020-05-25)</h2>
@@ -873,46 +937,46 @@
873937
can now omit punctation. So, for example, "202004181942" and
874938
"2020-04-18 19:42" mean the same thing.
875939
* Enhance backlink processing so that it works with Markdown-formatted
876940
tickets and so that it works for wiki pages.
877941
Ticket [a3572c6a5b47cd5a].
878
- <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to
942
+ <ul><li> "[/help/rebuild|fossil rebuild]" is needed to
879943
take full advantage of this fix. Fossil will continue
880944
to work without the rebuild, but the new backlinks will be missing.</ul>
881945
* The algorithm for finding the
882946
[./tech_overview.wiki#configloc|location of the configuration database]
883947
is enhanced to be XDG-compliant.
884948
* Add a hide/show feature to
885949
[./wikitheory.wiki#assocwiki|associated wiki] display on
886950
check-in and branch information pages.
887
- * Enhance the "[/help?cmd=info|fossil info]" command so that it
951
+ * Enhance the "[/help/info|fossil info]" command so that it
888952
works with no arguments even if not within an open check-out.
889953
* Many improvements to the forum and especially email notification
890954
of forum posts, in response to community feedback after switching
891955
SQLite support from a mailing list over to the forum.
892956
* Minimum length of a self-registered user ID increased from 3 to 6
893957
characters.
894958
* When the "vfx" query parameter is used on the
895
- "[/help?cmd=/timeline|/timeline]" page, it causes the complete
959
+ "[/help/www/timeline|/timeline]" page, it causes the complete
896960
text of forum posts to be displayed.
897
- * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful.
898
- * Expose the [/help?cmd=redirect-to-https|redirect-to-https]
899
- setting to the [/help?cmd=settings|settings] command.
961
+ * Rework the "[/help/grep|fossil grep]" command to be more useful.
962
+ * Expose the [/help/redirect-to-https|redirect-to-https]
963
+ setting to the [/help/settings|settings] command.
900964
* Improve support for CGI on IIS web servers.
901965
* The [./serverext.wiki|/ext page] can now render index files,
902966
in the same way as the embedded docs.
903967
* Most commands now support the Unix-conventional "<tt>--</tt>"
904968
flag to treat all following arguments as filenames
905969
instead of flags.
906
- * Added the [/help?cmd=mimetypes|mimetypes config setting]
970
+ * Added the [/help/mimetypes|mimetypes config setting]
907971
(versionable) to enable mimetype overrides and custom definitions.
908972
* Add an option on the /Admin/Timeline setup page to set a default
909973
timeline style other than "Modern".
910974
* In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
911975
of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
912976
into the check-in hash for the document currently being viewed.
913
- * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all
977
+ * Added the [/help/www/phantoms|/phantoms] webpage that shows all
914978
phantom artifacts.
915979
* Enhancements to phantom processing to try to reduce
916980
bandwidth-using chatter about phantoms on the sync protocol.
917981
* Security: Fossil now assumes that the schema of every
918982
database it opens has been tampered with by an adversary and takes
@@ -920,23 +984,23 @@
920984
* Security: Fossil now puts the Content-Security-Policy in the
921985
HTTP reply header, in addition to also leaving it in the
922986
HTML &lt;head&gt; section, so that it is always available, even
923987
if a custom skin overrides the HTML &lt;head&gt; and omits
924988
the CSP in the process.
925
- * Output of the [/help?cmd=diff|fossil diff -y] command automatically
989
+ * Output of the [/help/diff|fossil diff -y] command automatically
926990
adjusts according to the terminal width.
927991
* The Content-Security-Policy is now set using the
928
- [/help?cmd=default-csp|default-csp setting].
929
- * Merge conflicts caused via the [/help?cmd=merge|merge] and
930
- [/help?cmd=update|update] commands no longer leave temporary
992
+ [/help/default-csp|default-csp setting].
993
+ * Merge conflicts caused via the [/help/merge|merge] and
994
+ [/help/update|update] commands no longer leave temporary
931995
files behind unless the new <tt>--keep-merge-files</tt> flag
932996
is used.
933
- * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible
997
+ * The [/help/www/artifact_stats|/artifact_stats page] is now accessible
934998
to all users if the new "artifact_stats_enable" setting is turned
935999
on. There is a new checkbox under the /Admin/Access menu to turn
9361000
that capability on and off.
937
- * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing
1001
+ * Add the [/help/tls-config|fossil tls-config] command for viewing
9381002
the TLS configuration and the list of SSL Cert exceptions.
9391003
* Captchas all include a button to read the captcha using an audio
9401004
file, so that they can be completed by the visually impaired.
9411005
* Stop using the IP address as part of the login cookie.
9421006
* Bug fix: fix the SSL cert validation logic so that if an exception
@@ -957,27 +1021,27 @@
9571021
<h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2>
9581022
9591023
* (2.10.2): backport security fixes from 2.12.1
9601024
* (2.10.1): backport security fix for the "fossil git export" command.
9611025
* Added support for [./serverext.wiki|CGI-based Server Extensions].
962
- * Added the [/help?cmd=repolist-skin|repolist-skin] setting used to
1026
+ * Added the [/help/repolist-skin|repolist-skin] setting used to
9631027
add style to repository list pages.
9641028
* Enhance the hierarchical display of Forum threads to do less
9651029
indentation and to provide links back to the previous message
9661030
in the thread. Provide sequential numbers for all messages in
9671031
a forum thread.
9681032
* Add support for fenced code blocks and improved hyperlink
9691033
processing to the [/md_rules|markdown formatter].
9701034
* Add support for hyperlinks to wiki pages in the
9711035
[/md_rules|markdown formatter].
972
- * Enhance the [/help?cmd=/stat|/stat] page so that it gives the
1036
+ * Enhance the [/help/www/stat|/stat] page so that it gives the
9731037
option to show a breakdown of forum posts.
9741038
* The special check-in name "merge-in:BRANCH" means the source of
9751039
the most recent merge-in from the parent branch of BRANCH.
9761040
* Add hyperlinks to branch-diffs on the /info page and from
9771041
timelines of a branch.
978
- * Add graphical context on the [/help?cmd=/vdiff|/vdiff] page.
1042
+ * Add graphical context on the [/help/www/vdiff|/vdiff] page.
9791043
* Uppercase query parameters, POST parameters, and cookie names are
9801044
converted to all lowercase and entered into the parameter set,
9811045
instead of being discarded.
9821046
* Change the default [./hashpolicy.wiki|hash policy] to SHA3.
9831047
* Timeout [./server/any/cgi.md|CGI requests] after 300 seconds, or
@@ -990,14 +1054,14 @@
9901054
* Performance optimizations.
9911055
* Many documentation improvements.
9921056
9931057
<h2 id='v2_9'>Changes for Version 2.9 (2019-07-13)</h2>
9941058
995
- * Added the [/help?cmd=git|fossil git export] command and instructions
1059
+ * Added the [/help/git|fossil git export] command and instructions
9961060
for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project].
9971061
* Improved handling of relative hyperlinks on the
998
- [/help?cmd=/artifact|/artifact] pages for wiki. For example,
1062
+ [/help/www/artifact|/artifact] pages for wiki. For example,
9991063
hyperlinks and the lizard &lt;img&gt; now work correctly
10001064
for both [/artifact/2ff24ab0887cf522] and
10011065
[/doc/0d7ac90d575004c2415/www/index.wiki].
10021066
* Enhancements to the timeline graph layout, to show more information
10031067
with less clutter.
@@ -1006,28 +1070,28 @@
10061070
configuration.
10071071
* Copy buttons added to various check-in hash and branch name links.
10081072
* Double-clicking on a /timeline graph node now jumps to the /info page
10091073
for the check-in. So, repurpose the timestamp hyperlink to show all
10101074
activity around that check-in in time.
1011
- * Added the [/help?cmd=touch|fossil touch] command, and the --setmtime
1012
- option on the [/help?cmd=open|fossil open] and
1013
- [/help?cmd=update|fossil update] commands.
1075
+ * Added the [/help/touch|fossil touch] command, and the --setmtime
1076
+ option on the [/help/open|fossil open] and
1077
+ [/help/update|fossil update] commands.
10141078
* Many documentation enhancements.
1015
- * For the "[/help?cmd=update|fossil update]" and
1016
- "[/help?cmd=checkout|fossil checkout]" commands, if a
1079
+ * For the "[/help/update|fossil update]" and
1080
+ "[/help/checkout|fossil checkout]" commands, if a
10171081
managed file is removed because it is no longer part of the target
10181082
check-in and the directory containing the file is empty after the
10191083
file is removed and the directory is not the current working
1020
- directory and is not on the [/help?cmd=empty-dirs|empty-dirs]
1084
+ directory and is not on the [/help/empty-dirs|empty-dirs]
10211085
list, then also remove the directory.
10221086
* Update internal Unicode character tables, used in regular expression
10231087
handling, from version 11.0 to 12.1.
1024
- * In "[/help?cmd=regexp|fossil regexp]", "[/help?cmd=grep|fossil grep]"
1088
+ * In "[/help/regexp|fossil regexp]", "[/help/grep|fossil grep]"
10251089
and the TH1 "regexp" command, the -nocase option now removes multiple
10261090
diacritics from the same character (derived from SQLite's
10271091
remove_diacritics=2)
1028
- * Added the [/help?cmd=/secureraw|/secureraw] page that requires the
1092
+ * Added the [/help/www/secureraw|/secureraw] page that requires the
10291093
complete SHA1 or SHA3 hash, not just a prefix, before it will deliver
10301094
content.
10311095
* Accept purely numeric ISO8601 date/time strings as long as they
10321096
do not conflict with a hash. Example: "20190510134217" instead of
10331097
"2019-05-10 13:42:17". This helps keep URLs shorter and less
@@ -1040,19 +1104,19 @@
10401104
extra path element is not needed.
10411105
* If an automatic sync gets a permanent redirect request, then update
10421106
the saved remote URL to the new address.
10431107
* Temporary filenames (for example used for external "diff" commands)
10441108
try to preserve the suffix of the original file.
1045
- * Added the [/help?cmd=/thisdayinhistory|/thisdayinhistory] web page.
1046
- * Enhanced parsing of [/help?cmd=/timeline|/timeline] query parameters
1109
+ * Added the [/help/www/thisdayinhistory|/thisdayinhistory] web page.
1110
+ * Enhanced parsing of [/help/www/timeline|/timeline] query parameters
10471111
"ymd=", "ym=", and "yw=". All arguments are option (in which case they
10481112
default to the current time) and all accept ISO8601 date/times without
10491113
punctuation.
10501114
* Automatically disapprove pending moderation requests for a user when
10511115
that user is deleted. This helps in dealing with spam-bots.
10521116
* Improvements to the "Capability Summary" section in the
1053
- [/help?cmd=/secaudit0|Security Audit] web-page.
1117
+ [/help/www/secaudit0|Security Audit] web-page.
10541118
* Use new "ci-lock" and "ci-lock-failed" pragmas in the
10551119
[./sync.wiki|sync protocol] to try to prevent accident forks
10561120
caused by concurrent commits when operating in auto-sync mode.
10571121
* Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details])
10581122
that can cause repository databases to be overwritten with debugging
@@ -1153,11 +1217,11 @@
11531217
included in the header or footer.
11541218
* Add the [./backoffice.md|backoffice].
11551219
* Update internal Unicode character tables, used in regular expression
11561220
handling, from version 10.0 to 11.0.
11571221
* Improvements to the "Security Audit" administration page
1158
- * Add the [/help?cmd=branch|fossil branch current] command.
1222
+ * Add the [/help/branch|fossil branch current] command.
11591223
* Add the [./grep.md|grep] command.
11601224
* Update the built-in SQLite to version 3.25.1.
11611225
* Some code and interfaces are in place to support sending and
11621226
receiving email directly via SMTP, but this feature is not yet
11631227
complete or ready for production use.
@@ -1176,20 +1240,20 @@
11761240
same as "Verbose" in the previous release. The "Verbose" mode is
11771241
now like "Compact" except the extra check-in details are shown by
11781242
default.
11791243
* Add support for ETags:, Last-Modified:, and If-Modified-Since:
11801244
cache control mechanisms.
1181
- * Enhance the [/help?cmd=/tarball|/tarball],
1182
- [/help?cmd=/zip|/zip], and
1183
- [/help?cmd=/sqlar|/sqlar] pages so that the checkin
1245
+ * Enhance the [/help/www/tarball|/tarball],
1246
+ [/help/www/zip|/zip], and
1247
+ [/help/www/sqlar|/sqlar] pages so that the checkin
11841248
name to be downloaded can be expressed as part of the URI,
11851249
and without the need for query parameters.
1186
- * On the [/help?cmd=/timeline|/timeline] webpage, add the days=N
1250
+ * On the [/help/www/timeline|/timeline] webpage, add the days=N
11871251
query parameter and enhance the ymd=DATE and yw=DATE query parameters
11881252
to accept 'now' as an argument to show the latest day or week.
11891253
* In the web page that comes up in response to the
1190
- [/help?cmd=all|fossil all ui] command, show the last modification
1254
+ [/help/all|fossil all ui] command, show the last modification
11911255
time for each repository, and allow click-to-sort on the modification
11921256
time column.
11931257
* In the tarball cache replacement algorithm, give extra weight to
11941258
tarballs that have been accessed more than once.
11951259
* Additional defenses against web-based attacks. There have not been
@@ -1236,35 +1300,35 @@
12361300
to define their own URLs on the web interface that are rewritten to
12371301
built-in URLs with specific parameters. Create and configure URL Aliases
12381302
using the /Setup/URL_Aliases menu option in the web interface.
12391303
* Add tech-note search capability.
12401304
* Add the -r|--revision and -o|--origin options to the
1241
- [/help?cmd=annotate|annotate] command.
1242
- * Add the origin= query parameter to the [/help?cmd=/annotate|/annotate]
1305
+ [/help/annotate|annotate] command.
1306
+ * Add the origin= query parameter to the [/help/www/annotate|/annotate]
12431307
webpage.
1244
- * The [/help?cmd=annotate|fossil annotate] command and the
1245
- [/help?cmd=/annotate|/annotate] web page go backwards in time as far
1308
+ * The [/help/annotate|fossil annotate] command and the
1309
+ [/help/www/annotate|/annotate] web page go backwards in time as far
12461310
as can be computed in 30 milliseconds by default, rather than stopping
12471311
after 20 steps. The new limit= query parameter or the --limit command-line
12481312
option can be used to alter this timeout.
12491313
* Provide separate [/help#settings|on-line help screens for each setting].
12501314
* Back out support for the --no-dir-symlinks option
12511315
* Remove support from the legacy configuration sync protocol. The only
12521316
way now to do a configuration push or pull is to use the new protocol that
12531317
was added in 2011.
1254
- * Add the from= and to= query parameters to [/help?cmd=/fdiff|/fdiff]
1318
+ * Add the from= and to= query parameters to [/help/www/fdiff|/fdiff]
12551319
in order to get a diff of two files in the same check-in.
12561320
* Fix the "ssh://" protocol to prevent an attack whereby the attacker convinces
12571321
a victim to run a "clone" with a dodgy URL and thereby gains access to their
12581322
system.
12591323
* Provide a checkbox that will temporarily disable all ad-units.
1260
- * Improvements to the [/help?cmd=/stat|/stat] page
1261
- * Various new hyperlinks to the [/help?cmd=/bloblist|/bloblist]
1262
- and [/help?cmd=/bigbloblist|/bigbloblist] pages.
1263
- * Correct the [/help?cmd=/doc|/doc] page to support read-only repositories.
1264
- * Correct [/help?cmd=/zip|/zip], [/help?cmd=/tarball|/tarball],
1265
- [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to
1324
+ * Improvements to the [/help/www/stat|/stat] page
1325
+ * Various new hyperlinks to the [/help/www/bloblist|/bloblist]
1326
+ and [/help/www/bigbloblist|/bigbloblist] pages.
1327
+ * Correct the [/help/www/doc|/doc] page to support read-only repositories.
1328
+ * Correct [/help/www/zip|/zip], [/help/www/tarball|/tarball],
1329
+ [/help/zip|zip], and [/help/tarball|tarball] pages and commands to
12661330
honor the versioned manifest setting when outside of an open checkout
12671331
directory.
12681332
* The admin-log and access-log settings are now on by default for
12691333
new repositories.
12701334
* Update the built-in SQLite to version 3.21.0.
@@ -1272,34 +1336,34 @@
12721336
<h2 id='v2_3'>Changes for Version 2.3 (2017-07-21)</h2>
12731337
12741338
* Update the built-in SQLite to version 3.20.0 (beta).
12751339
* Update internal Unicode character tables, used in regular expression
12761340
handling, from version 9.0 to 10.0.
1277
- * Show the last-sync-URL on the [/help?cmd=/urllist|/urllist] page.
1341
+ * Show the last-sync-URL on the [/help/www/urllist|/urllist] page.
12781342
* Added the "Event Summary" activity report.
12791343
[/reports?type=ci&view=lastchng|example]
12801344
* Added the "Security Audit" page, available to administrators only
12811345
* Added the Last Login time to the user list page, for administrators only
1282
- * Added the --numstat option to the [/help?cmd=diff|fossil diff] command
1346
+ * Added the --numstat option to the [/help/diff|fossil diff] command
12831347
* Limit the size of the heap and stack on unix systems, as a proactive
12841348
defense against the
12851349
[https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt|Stack Clash]
12861350
attack.
12871351
* Fix "database locked" warnings caused by "PRAGMA optimize".
12881352
* Fix a potential XSS vulnerability on the
1289
- [/help?cmd=/help|/help] webpage.
1353
+ [/help/www/help|/help] webpage.
12901354
* Documentation updates
12911355
12921356
<h2 id='v2_2'>Changes for Version 2.2 (2017-04-11)</h2>
12931357
12941358
* GIT comment tags are now handled by Fossil during import/export.
12951359
* Show the content of README files on directory listings.
12961360
([/file/skins|example])
12971361
* Support for Basic Authentication if enabled (default off).
12981362
* Show the hash algorithms used on the
1299
- [/help?cmd=/rcvfromlist|/rcvfromlist] page.
1300
- * The [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] pages
1363
+ [/help/www/rcvfromlist|/rcvfromlist] page.
1364
+ * The [/help/www/tarball|/tarball] and [/help/www/zip|/zip] pages
13011365
now use the the r= query parameter
13021366
to select which check-in to deliver. The uuid= query parameter
13031367
is still accepted for backwards compatibility.
13041368
* Update the built-in SQLite to version 3.18.0.
13051369
* Run "[https://www.sqlite.org/pragma.html#pragma_optimize|PRAGMA optimize]"
@@ -1308,12 +1372,12 @@
13081372
<h2 id='v2_1'>Changes for Version 2.1 (2017-03-10)</h2>
13091373
13101374
* Add support for [./hashpolicy.wiki|hash policies] that control which
13111375
of the Hardened-SHA1 or SHA3-256 algorithms is used to name new
13121376
artifacts.
1313
- * Add the "gshow" and "gcat" subcommands to [/help?cmd=stash|fossil stash].
1314
- * Add the [/help?cmd=/juvlist|/juvlist] web page and use it to construct
1377
+ * Add the "gshow" and "gcat" subcommands to [/help/stash|fossil stash].
1378
+ * Add the [/help/www/juvlist|/juvlist] web page and use it to construct
13151379
the [/uv/download.html|Download Page] of the Fossil self-hosting website
13161380
using Ajax.
13171381
13181382
<h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2>
13191383
@@ -1321,23 +1385,23 @@
13211385
[https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
13221386
implementation by Marc Stevens and Dan Shumow.
13231387
* Add the ability to read and understand
13241388
[./fileformat.wiki#names|artifact names] that are based on SHA3-256
13251389
rather than SHA1, but do not actually generate any such names.
1326
- * Added the [/help?cmd=sha3sum|sha3sum] command.
1390
+ * Added the [/help/sha3sum|sha3sum] command.
13271391
* Update the built-in SQLite to version 3.17.0.
13281392
13291393
<h2 id='v1_37'>Changes for Version 1.37 (2017-01-16)</h2>
13301394
13311395
* Add checkbox widgets to various web pages. See [/technote/8d18bf27e9|
13321396
this technote] for more information. To get the checkboxes to look as
13331397
intended, you must update the CSS in your repository and all clones.
13341398
* Add the [/help/all|fossil all ui] command
1335
- * Add the [/help?cmd=/file|/file] webpage
1336
- * Enhance the [/help?cmd=/brlist|/brlist] webpage to make use of branch colors.
1399
+ * Add the [/help/www/file|/file] webpage
1400
+ * Enhance the [/help/www/brlist|/brlist] webpage to make use of branch colors.
13371401
* Add support for the ms=EXACT|LIKE|GLOB|REGEXP query parameter on the
1338
- [/help?cmd=/timeline|/timeline] webpage, with associated form widgets.
1402
+ [/help/www/timeline|/timeline] webpage, with associated form widgets.
13391403
* Enhance the [/help/changes|changes] and [/help/status|status] commands
13401404
with many new filter options so that specific kinds of changes can be
13411405
found without having to pipe through grep or sed.
13421406
* Enhanced the [/help/sqlite3|fossil sql] command so that it opens the
13431407
[./tech_overview.wiki#localdb|checkout database] and the
@@ -1350,11 +1414,11 @@
13501414
</ul>
13511415
* Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep
13521416
crnl-glob as a compatibility alias.
13531417
* Added the --command option to the [/help/diff|diff] command.
13541418
* Fix a C99-ism that prevents the 1.36 release from building with MSVC.
1355
- * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
1419
+ * Fix [/help/ticket|ticket set] when using the "+" prefix with fields
13561420
from the "ticketchng" table.
13571421
* Remove the "fusefs" command from builds that do not have the underlying
13581422
support enabled.
13591423
* Fixes for incremental git import/export.
13601424
* Minor security enhancements to
@@ -1364,58 +1428,58 @@
13641428
13651429
13661430
<h2 id='v1_36'>Changes for Version 1.36 (2016-10-24)</h2>
13671431
13681432
* Add support for [./unvers.wiki|unversioned content],
1369
- the [/help?cmd=unversioned|fossil unversioned] command and the
1370
- [/help?cmd=/uv|/uv] and [/uvlist] web pages.
1433
+ the [/help/unversioned|fossil unversioned] command and the
1434
+ [/help/www/uv|/uv] and [/uvlist] web pages.
13711435
* The [/uv/download.html|download page] is moved into
13721436
[./unvers.wiki|unversioned content] so that the self-hosting Fossil
13731437
websites no longer uses any external content.
13741438
* Added the "Search" button to the graphical diff generated by
1375
- the --tk option on the [/help?cmd=diff|diff] command.
1439
+ the --tk option on the [/help/diff|diff] command.
13761440
* Added the "--checkin VERSION" option to the
1377
- [/help?cmd=diff|diff] command.
1378
- * Various performance enhancements to the [/help?cmd=diff|diff] command.
1441
+ [/help/diff|diff] command.
1442
+ * Various performance enhancements to the [/help/diff|diff] command.
13791443
* Update internal Unicode character tables, used in regular expression
13801444
handling, from version 8.0 to 9.0.
13811445
* Update the built-in SQLite to version 3.15. Fossil now requires
13821446
the SQLITE_DBCONFIG_MAINDBNAME interface of SQLite which is only available
13831447
in SQLite version 3.15 and later and so Fossil will not work with
13841448
earlier SQLite versions.
13851449
* Fix [https://www.mail-archive.com/[email protected]/msg23618.html|multi-line timeline bug]
1386
- * Enhance the [/help?cmd=purge|fossil purge] command.
1387
- * New command [/help?cmd=shell|fossil shell].
1450
+ * Enhance the [/help/purge|fossil purge] command.
1451
+ * New command [/help/shell|fossil shell].
13881452
* SQL parameters whose names are all lower-case in Ticket Report SQL
13891453
queries are filled in using HTTP query parameter values.
13901454
* Added support for [./childprojects.wiki|child projects] that are
13911455
able to pull from their parent but not push.
13921456
* Added the -nocomplain option to the TH1 "query" command.
13931457
* Added support for the chng=GLOBLIST query parameter on the
1394
- [/help?cmd=/timeline|/timeline] webpage.
1458
+ [/help/www/timeline|/timeline] webpage.
13951459
13961460
<h2 id='v1_35'>Changes for Version 1.35 (2016-06-14)</h2>
13971461
13981462
* Enable symlinks by default on all non-Windows platforms.
13991463
* Enhance the [/md_rules|Markdown formatting] so that hyperlinks that begin
14001464
with "/" are relative to the root of the Fossil repository.
1401
- * Rework the [/help?cmd=/setup_ulist|/setup_list page] (the User List page)
1465
+ * Rework the [/help/www/setup_ulist|/setup_list page] (the User List page)
14021466
to display all users in a click-to-sort table.
14031467
* Fix backslash-octal escape on filenames while importing from git
14041468
* When markdown documents begin with &lt;h1&gt; HTML elements, use that
14051469
header at the document title.
1406
- * Added the [/help?cmd=/bigbloblist|/bigbloblist page].
1407
- * Enhance the [/help?cmd=/finfo|/finfo page] so that when it is showing
1470
+ * Added the [/help/www/bigbloblist|/bigbloblist page].
1471
+ * Enhance the [/help/www/finfo|/finfo page] so that when it is showing
14081472
the ancestors of a particular file version, it only shows direct
14091473
ancestors and omits changes on branches, thus making it show the same set
1410
- of ancestors that are used for [/help?cmd=/blame|/blame].
1411
- * Added the --page option to the [/help?cmd=ui|fossil ui] command
1412
- * Added the [/help?cmd=bisect|fossil bisect ui] command
1413
- * Enhanced the [/help?cmd=diff|fossil diff] command so that it accepts
1474
+ of ancestors that are used for [/help/www/blame|/blame].
1475
+ * Added the --page option to the [/help/ui|fossil ui] command
1476
+ * Added the [/help/bisect|fossil bisect ui] command
1477
+ * Enhanced the [/help/diff|fossil diff] command so that it accepts
14141478
directory names as arguments and computes diffs on all files contained
14151479
within those directories.
1416
- * Fix the [/help?cmd=add|fossil add] command so that it shows "SKIP" for
1480
+ * Fix the [/help/add|fossil add] command so that it shows "SKIP" for
14171481
files added that were already under management.
14181482
* TH1 enhancements:
14191483
<ul><li>Add <nowiki>[array exists]</nowiki> command.</li>
14201484
<li>Add minimal <nowiki>[array names]</nowiki> command.</li>
14211485
<li>Add tcl_platform(engine) and tcl_platform(platform) array
@@ -1423,32 +1487,32 @@
14231487
</ul>
14241488
* Get autosetup working with MinGW.
14251489
* Fix autosetup detection of zlib in the source tree.
14261490
* Added autosetup detection of OpenSSL when it may be present under the
14271491
"compat" subdirectory of the source tree.
1428
- * Added the [/help?cmd=reparent|fossil reparent] command
1429
- * Added --include and --exclude options to [/help?cmd=tarball|fossil tarball]
1430
- and [/help?cmd=zip|fossil zip] and the in= and ex= query parameters to the
1431
- [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] web pages.
1492
+ * Added the [/help/reparent|fossil reparent] command
1493
+ * Added --include and --exclude options to [/help/tarball|fossil tarball]
1494
+ and [/help/zip|fossil zip] and the in= and ex= query parameters to the
1495
+ [/help/www/tarball|/tarball] and [/help/www/zip|/zip] web pages.
14321496
* Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
14331497
* If the FOSSIL_PWREADER environment variable is set, then use the program it
14341498
names in place of getpass() to read passwords and passphrases
14351499
* Option --baseurl now works on Windows.
14361500
* Numerous documentation improvements.
14371501
* Update the built-in SQLite to version 3.13.0.
14381502
14391503
<h2 id='v1_34'>Changes for Version 1.34 (2015-11-02)</h2>
14401504
1441
- * Make the [/help?cmd=clean|fossil clean] command undoable for files less
1505
+ * Make the [/help/clean|fossil clean] command undoable for files less
14421506
than 10MiB.
14431507
* Update internal Unicode character tables, used in regular expression
14441508
handling, from version 7.0 to 8.0.
1445
- * Add the new [/help?cmd=amend|amend] command which is used to modify
1509
+ * Add the new [/help/amend|amend] command which is used to modify
14461510
tags of a "check-in".
1447
- * Fix bug in [/help?cmd=import|import] command, handling version 3 of
1511
+ * Fix bug in [/help/import|import] command, handling version 3 of
14481512
the svndump format for subversion.
1449
- * Add the [/help?cmd=all|all cache] command.
1513
+ * Add the [/help/all|all cache] command.
14501514
* TH1 enhancements:
14511515
<ul><li>Add minimal <nowiki>[lsearch]</nowiki> command. Only exact
14521516
case-sensitive matching is supported.</li>
14531517
<li>Add the <nowiki>[glob_match]</nowiki>, <nowiki>[markdown]</nowiki>,
14541518
<nowiki>[dir]</nowiki>, and <nowiki>[encode64]</nowiki> commands.</li>
@@ -1455,106 +1519,106 @@
14551519
<li>Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> commands to
14561520
the Tcl integration subsystem.</li>
14571521
<li>Add 'double', 'integer', and 'list' classes to the
14581522
<nowiki>[string is]</nowiki> command.</li>
14591523
</ul>
1460
- * Add the --undo option to the [/help?cmd=diff|diff] command.
1524
+ * Add the --undo option to the [/help/diff|diff] command.
14611525
* Build-in Antirez's "linenoise" command-line editing library for use with
1462
- the [/help?cmd=sqlite3|fossil sql] command on Unix platforms.
1463
- * Add [/help?cmd=stash|stash cat] as an alias for the
1464
- [/help?cmd=stash|stash show] command.
1465
- * Automatically pull before [/help?cmd=merge|fossil merge] when auto-sync
1526
+ the [/help/sqlite3|fossil sql] command on Unix platforms.
1527
+ * Add [/help/stash|stash cat] as an alias for the
1528
+ [/help/stash|stash show] command.
1529
+ * Automatically pull before [/help/merge|fossil merge] when auto-sync
14661530
is enabled.
1467
- * Fix --hard option to [/help?cmd=mv|fossil mv] and [/help?cmd=rm|fossil rm]
1531
+ * Fix --hard option to [/help/mv|fossil mv] and [/help/rm|fossil rm]
14681532
to enable them to work properly with certain relative paths.
14691533
* Change the mimetype for ".n" and ".man" files to text/plain.
1470
- * Display improvements in the [/help?cmd=bisect|fossil bisect chart] command.
1534
+ * Display improvements in the [/help/bisect|fossil bisect chart] command.
14711535
* Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5
14721536
support (both currently unused within Fossil).
14731537
14741538
<h2 id='v1_33'>Changes for Version 1.33 (2015-05-23)</h2>
1475
- * Improved fork detection on [/help?cmd=update|fossil update],
1476
- [/help?cmd=status|fossil status] and related commands.
1539
+ * Improved fork detection on [/help/update|fossil update],
1540
+ [/help/status|fossil status] and related commands.
14771541
* Change the default skin to what used to be called "San Francisco Modern".
14781542
* Add the [/repo-tabsize] web page
1479
- * Add [/help?cmd=import|fossil import --svn], for importing a subversion
1543
+ * Add [/help/import|fossil import --svn], for importing a subversion
14801544
repository into fossil which was exported using "svnadmin dump".
1481
- * Add the "--compress-only" option to [/help?cmd=rebuild|fossil rebuild].
1545
+ * Add the "--compress-only" option to [/help/rebuild|fossil rebuild].
14821546
* Use a pie chart on the [/reports?view=byuser] page.
1483
- * Enhanced [/help?cmd=clean|fossil clean --verily] so that it ignores
1547
+ * Enhanced [/help/clean|fossil clean --verily] so that it ignores
14841548
keep-glob and ignore-glob settings. Added the -x alias for --verily.
1485
- * Add the --soft and --hard options to [/help?cmd=rm|fossil rm] and
1486
- [/help?cmd=mv|fossil mv]. The default is still --soft, but that is
1549
+ * Add the --soft and --hard options to [/help/rm|fossil rm] and
1550
+ [/help/mv|fossil mv]. The default is still --soft, but that is
14871551
now configurable at compile-time or by the mv-rm-files setting.
14881552
* Improved ability to [./customgraph.md|customize the timeline graph].
14891553
* Improvements to the [/sitemap] page.
1490
- * Automatically adjust the [/help?cmd=timeline|CLI timeline] to the terminal
1554
+ * Automatically adjust the [/help/timeline|CLI timeline] to the terminal
14911555
width on Linux.
14921556
* Added <nowiki>[info commands] and [info vars]</nowiki> commands to TH1.
14931557
These commands perform the same function as their Tcl counterparts,
14941558
except they do not accept a pattern argument.
14951559
* Fix some obscure issues with TH1 expression processing.
14961560
* Fix titles in search results for documents that are not wiki, markdown,
14971561
or HTML.
14981562
* Formally translate TH1 to Tcl return codes and vice-versa, where
14991563
necessary, in the Tcl integration subsystem.
1500
- * Add [/help?cmd=leaves|fossil leaves -multiple], for finding multiple
1564
+ * Add [/help/leaves|fossil leaves -multiple], for finding multiple
15011565
leaves on the same branch.
15021566
* Added the "Blitz" skin option.
15031567
* Removed the ".fossil-settings/keep-glob" file. It should not have been
15041568
checked into the repository.
15051569
* Update the built-in SQLite to version 3.8.10.2.
1506
- * Make [/help?cmd=open|fossil open] honor ".fossil-settings/allow-symlinks".
1507
- * Allow [/help?cmd=add|fossil add] to be used on symlinks to nonexistent or
1508
- unreadable files in the same way as [/help?cmd=addremove|fossil addremove].
1570
+ * Make [/help/open|fossil open] honor ".fossil-settings/allow-symlinks".
1571
+ * Allow [/help/add|fossil add] to be used on symlinks to nonexistent or
1572
+ unreadable files in the same way as [/help/addremove|fossil addremove].
15091573
* Added fork warning to be issued if sync produced a fork
1510
- * Update the [/help?cmd=/info|info] page to report when a file becomes a
1574
+ * Update the [/help/www/info|info] page to report when a file becomes a
15111575
symlink. Additionally show the UUID for files whose types have changed
15121576
without changing contents or symlink target.
1513
- * Have [/help?cmd=changes|fossil changes] and
1514
- [/help?cmd=status|fossil status] report when executable or symlink status
1577
+ * Have [/help/changes|fossil changes] and
1578
+ [/help/status|fossil status] report when executable or symlink status
15151579
changes on otherwise unmodified files.
1516
- * Permit filtering weekday and file [/help?cmd=/reports|reports] by user.
1580
+ * Permit filtering weekday and file [/help/www/reports|reports] by user.
15171581
Also ensure the user parameter is preserved when changing types. Add a
15181582
field for direct entry of the user name to each applicable report.
1519
- * Create parent directories of [/help?cmd=settings|empty-dirs] if they don't
1583
+ * Create parent directories of [/help/settings|empty-dirs] if they don't
15201584
already exist.
15211585
* Inhibit timeline links to wiki pages that have been deleted.
15221586
15231587
<h2 id='v1_33'>Changes for Version 1.32 (2015-03-14)</h2>
1524
- * When creating a new repository using [/help?cmd=init|fossil init], ensure
1588
+ * When creating a new repository using [/help/init|fossil init], ensure
15251589
that the new repository is fully compatible with historical versions of
15261590
Fossil by having a valid manifest as RID 1.
15271591
* Anti-aliased rendering of arrowheads on timeline graphs.
15281592
* Added vi/less-style key bindings to the --tk diff GUI.
15291593
* Documentation updates to fix spellings and changes all "checkins" to
15301594
"check-ins".
15311595
* Add the --repolist option to server commands such as
1532
- [/help?cmd=server|fossil server] or [/help?cmd=http|fossil http].
1596
+ [/help/server|fossil server] or [/help/http|fossil http].
15331597
* Added the "Xekri" skin.
15341598
* Enhance the "ln=" query parameter on artifact displays to accept multiple
15351599
ranges, separate by spaces (or "+" when URL-encoded).
1536
- * Added [/help?cmd=forget|fossil forget] as an alias for
1537
- [/help?cmd=rm|fossil rm].
1600
+ * Added [/help/forget|fossil forget] as an alias for
1601
+ [/help/rm|fossil rm].
15381602
15391603
<h2 id='v1_31'>Changes For Version 1.31 (2015-02-23)</h2>
15401604
* Change the auxiliary schema by adding columns MLINK.ISAUX and MLINK.PMID
15411605
columns to the schema, to support better drawing of file change graphs.
1542
- A [/help?cmd=rebuild|fossil rebuild] is recommended but is not required.
1606
+ A [/help/rebuild|fossil rebuild] is recommended but is not required.
15431607
so that the new graph drawing logic can work effectively.
15441608
* Added [/search|search] over Check-in comments, Documents, Tickets and
15451609
Wiki. Disabled by default. The search can be either a full-scan or it
15461610
can use an index that is kept up-to-date automatically. The new
1547
- /srchsetup web-page and the [/help?cmd=fts-config|fts-config] command
1611
+ /srchsetup web-page and the [/help/fts-config|fts-config] command
15481612
were added to help configure the search capability. Expect further
15491613
enhancements to the search capabilities in subsequent releases.
15501614
* Added form elements to some submenus (in particular the /timeline)
15511615
for easier operation.
1552
- * Added the --ifneeded option to [/help?cmd=rebuild|fossil rebuild].
1616
+ * Added the --ifneeded option to [/help/rebuild|fossil rebuild].
15531617
* Added "override skins" using the "skin:" line of the CGI script or
1554
- using the --skin LABEL option on the [/help?cmd=server|server],
1555
- [/help?cmd=ui|ui], or [/help?cmd=http|http] commands.
1618
+ using the --skin LABEL option on the [/help/server|server],
1619
+ [/help/ui|ui], or [/help/http|http] commands.
15561620
* Embedded html documents that begin with
15571621
&lt;doc class="fossil-doc"&gt; are displayed with standard
15581622
headers and footers added.
15591623
* Allow &lt;div style='...'&gt; markup in [/wiki_rules|wiki].
15601624
* Renamed "Events" to "Technical Notes", while updating the technote
@@ -1561,24 +1625,24 @@
15611625
display and control pages. Add support for technotes as plain text
15621626
or as Markdown.
15631627
* Added the [/md_rules] pages containing summary instructions on the
15641628
Markdown format.
15651629
* Added the --repolist and --nojail options to the various server commands
1566
- (ex: [/help?cmd=server|fossil server]).
1567
- * Added the [/help?cmd=all|fossil all add] subcommand to "fossil all".
1630
+ (ex: [/help/server|fossil server]).
1631
+ * Added the [/help/all|fossil all add] subcommand to "fossil all".
15681632
* Improvements to the /login page. Some hyperlinks to pages that require
15691633
"anonymous" privileges are displayed even if the current user is "nobody"
15701634
but automatically redirect to /login.
1571
- * The [/help?cmd=/doc|/doc] web-page will now try to deliver the file
1635
+ * The [/help/www/doc|/doc] web-page will now try to deliver the file
15721636
"404.md" from the top-level directory (if such a file exists) in
15731637
place of its built-in 404 text.
15741638
* Download of Tarballs and ZIP Archives by user "nobody" is now enabled
15751639
by default in new repositories.
15761640
* Enhancements to the table sorting controls. More display tables
15771641
are now sortable.
1578
- * Add IPv6 support to [/help?cmd=sync|fossil sync] and
1579
- [/help?cmd=clone|fossil clone]
1642
+ * Add IPv6 support to [/help/sync|fossil sync] and
1643
+ [/help/clone|fossil clone]
15801644
* Add more skins such as "San Francisco Modern" and "Eagle".
15811645
* During shutdown, check to see if the check-out database (".fslckout")
15821646
contains a lot of free space, and if it does, VACUUM it.
15831647
* Added the [/mimetype_list] page.
15841648
* Added the [/hash-collisions] page.
@@ -1586,14 +1650,14 @@
15861650
ticket reports.
15871651
* Break out the components (css, footer, and header) for the
15881652
various built-in skins into separate files in the source tree.
15891653
15901654
<h2 id='v1_30'>Changes For Version 1.30 (2015-01-19)</h2>
1591
- * Added the [/help?cmd=bundle|fossil bundle] command.
1592
- * Added the [/help?cmd=purge|fossil purge] command.
1593
- * Added the [/help?cmd=publish|fossil publish] command.
1594
- * Added the [/help?cmd=unpublished|fossil unpublished] command.
1655
+ * Added the [/help/bundle|fossil bundle] command.
1656
+ * Added the [/help/purge|fossil purge] command.
1657
+ * Added the [/help/publish|fossil publish] command.
1658
+ * Added the [/help/unpublished|fossil unpublished] command.
15951659
* Enhance the [/tree] webpage to show the age of each file with the option
15961660
to sort by age.
15971661
* Enhance the [/brlist] webpage to show additional information about each branch
15981662
and to be sortable by clicking on column headers.
15991663
* Add support for Docker. Just install docker and type
@@ -1603,17 +1667,17 @@
16031667
copies of all historical check-ins. This only works on systems that
16041668
support FuseFS.
16051669
* Add the administrative log that records all configuration.
16061670
* Added the [/sitemap] webpage.
16071671
* Added the [/bloblist] web page.
1608
- * Let [/help?cmd=new|fossil new] no longer create an initial empty commit
1672
+ * Let [/help/new|fossil new] no longer create an initial empty commit
16091673
by default. The first commit after checking out an empty repository will
16101674
become the initial commit.
1611
- * Added the [/help?cmd=all|fossil all dbstat] and
1612
- [/help?cmd=all|fossil all info] commands.
1675
+ * Added the [/help/all|fossil all dbstat] and
1676
+ [/help/all|fossil all info] commands.
16131677
* Update SQLite to version 3.8.8.
1614
- * Added the --verily option to the [/help?cmd=clean|fossil clean] command.
1678
+ * Added the --verily option to the [/help/clean|fossil clean] command.
16151679
* Add the "autosync-tries" setting to control the number of autosync attempts
16161680
before returning an error.
16171681
* Added a compile-time option (--with-miniz) to build using miniz instead
16181682
of zlib. Disabled by default.
16191683
* Support customization of commands and webpages, including the ability to
@@ -1623,12 +1687,12 @@
16231687
[trace], [getParameter], [setParameter], [artifact], and
16241688
[globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks.
16251689
* Automatically adjust the width of command-line timeline output according to the
16261690
detected width of the terminal.
16271691
* Prompt the user to optionally fix invalid UTF-8 at check-in.
1628
- * Added a line-number toggle option to the [/help?cmd=/info|/info]
1629
- and [/help?cmd=/artifact|/artifact] pages.
1692
+ * Added a line-number toggle option to the [/help/www/info|/info]
1693
+ and [/help/www/artifact|/artifact] pages.
16301694
* Most commands now issue errors rather than silently ignoring unrecognized
16311695
command-line options.
16321696
* Use full 40-character SHA1 hashes (instead of abbreviations) in most
16331697
internal URLs.
16341698
* The "ssh:" sync method on Windows now uses "plink.exe" instead of "ssh" as
@@ -1642,11 +1706,11 @@
16421706
* Fix a rare and long-standing sync protocol bug
16431707
that would silently prevent the sync from running to completion. Before
16441708
this bug-fix it was sometimes necessary to do "fossil sync --verily" to
16451709
get two repositories in sync.
16461710
* Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful
1647
- for ad hoc queries in the [/help?cmd=sqlite3|fossil sql] interface,
1711
+ for ad hoc queries in the [/help/sqlite3|fossil sql] interface,
16481712
and also used internally.
16491713
* Added the "$secureurl" TH1 variable for use in headers and footers.
16501714
* (Internal:) Add the ability to include resources as separate files in the
16511715
source tree that are converted into constant byte arrays in the compiled
16521716
binary. Use this feature to store the Tk script that implements the --tk
@@ -1666,143 +1730,143 @@
16661730
* The [/reports] page now requires Read ("o") permissions. The "byweek"
16671731
report now properly propagates the selected year through the event type
16681732
filter links.
16691733
* The [/help/info | info command] now shows leaf status of the checkout.
16701734
* Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
1671
- * Add option --empty to the "[/help?cmd=open | fossil open]" command.
1672
- * Enhanced [/help?cmd=/fileage|the fileage page] to support a glob parameter.
1735
+ * Add option --empty to the "[/help/open | fossil open]" command.
1736
+ * Enhanced [/help/www/fileage|the fileage page] to support a glob parameter.
16731737
* Add -w|--ignore-all-space and -Z|--ignore-trailing-space options to
1674
- [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame],
1675
- [/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff].
1676
- * Add --strip-trailing-cr option to [/help?cmd=diff|fossil (g)diff] and
1677
- [/help?cmd=stash|fossil stash diff].
1738
+ [/help/annotate|fossil annotate], [/help/blame|fossil blame],
1739
+ [/help/diff|fossil (g)diff], [/help/stash|fossil stash diff].
1740
+ * Add --strip-trailing-cr option to [/help/diff|fossil (g)diff] and
1741
+ [/help/stash|fossil stash diff].
16781742
* Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
16791743
and /vdiff UI pages.
16801744
* Enhance [/reports?view=byweekday|/reports] with a "byweekday" view.
1681
- * Enhance the [/help?cmd=cat|fossil cat] command so that it works outside
1745
+ * Enhance the [/help/cat|fossil cat] command so that it works outside
16821746
of a checkout when using the -R command-line option.
16831747
* Use full-length SHA1 hashes, not abbreviations, in most hyperlinks.
16841748
* Correctly render the &lt;title&gt; markup on wiki pages in the
1685
- [/help?cmd=/artifact|/artifact] webpage.
1686
- * Enhance the [/help?cmd=whatis|fossil whatis] command to report on attachments
1687
- and cluster artifacts. Added the [/help?cmd=test-whatis-all] command for
1749
+ [/help/www/artifact|/artifact] webpage.
1750
+ * Enhance the [/help/whatis|fossil whatis] command to report on attachments
1751
+ and cluster artifacts. Added the [/help/test-whatis-all] command for
16881752
testing purposes.
1689
- * Add support for HTTP Basic Authentication on [/help?cmd=clone|clone] and
1690
- [/help?cmd=sync|sync].
1691
- * Fix the [/help?cmd=stash|stash] so that it remembers added files and re-adds
1753
+ * Add support for HTTP Basic Authentication on [/help/clone|clone] and
1754
+ [/help/sync|sync].
1755
+ * Fix the [/help/stash|stash] so that it remembers added files and re-adds
16921756
them when the stash is applied.
16931757
* Fix the server so that it avoids writing to the database (and thus avoids
16941758
database locking issues) on a
1695
- [/help?cmd=pull|pull] or [/help?cmd=clone|clone].
1759
+ [/help/pull|pull] or [/help/clone|clone].
16961760
* Add support for [./server.wiki#loadmgmt|server load management] using both
1697
- a cache of expensive pages (the [/help?cmd=cache|fossil cache] command)
1761
+ a cache of expensive pages (the [/help/cache|fossil cache] command)
16981762
and by rejecting expensive page requests when the server load average is too
16991763
high.
1700
- * Add the [/help?cmd=praise|fossil praise] command as an alias for
1701
- [/help?cmd=blame|fossil blame] for subversion compatibility.
1702
- * Enhance the [/help?cmd=test-diff|fossil test-diff] command with -y or --tk
1764
+ * Add the [/help/praise|fossil praise] command as an alias for
1765
+ [/help/blame|fossil blame] for subversion compatibility.
1766
+ * Enhance the [/help/test-diff|fossil test-diff] command with -y or --tk
17031767
options so that it shows both filenames above their respective columns in
17041768
the side-by-side diff output.
1705
- * Issue a warning if a [/help?cmd=add|fossil add] command tries to add a file
1769
+ * Issue a warning if a [/help/add|fossil add] command tries to add a file
17061770
that matches the ignore-glob.
1707
- * Add option -W|--width to "[/help?cmd=stash|fossil stash ls]"
1708
- and "[/help?cmd=leaves|fossil leaves]" commands.
1771
+ * Add option -W|--width to "[/help/stash|fossil stash ls]"
1772
+ and "[/help/leaves|fossil leaves]" commands.
17091773
* Enhance support for running as the root user. Now works on Haiku.
1710
- * Added the <tt>-empty</tt> option to [/help?cmd=new|fossil new], which
1774
+ * Added the <tt>-empty</tt> option to [/help/new|fossil new], which
17111775
causes it to not create an initial empty commit. The first commit after
17121776
checking out a repo created this way will become the initial commit.
17131777
* Enhance sync operations by committing each round-trip to minimize number
17141778
of retransmits when autosync fails. Include option for
1715
- [/help?cmd=update| fossil update] and [/help?cmd=merge| fossil merge] to
1779
+ [/help/update| fossil update] and [/help/merge| fossil merge] to
17161780
continue even if missing content.
17171781
* Minor portability fixes for platforms where the char type is unsigned
17181782
by default.
17191783
17201784
<h2>Changes For Version 1.28 (2014-01-27)</h2>
1721
- * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
1785
+ * Enhance [/help/www/reports | /reports] to support event type filtering.
17221786
* When cloning a repository, the user name passed via the URL (if any)
17231787
is now used as the default local admin user's name.
17241788
* Enhance the SSH transport mechanism so that it runs a single instance of
17251789
the "fossil" executable on the remote side, obviating the need for a shell
17261790
on the remote side. Some users may need to add the "?fossil=/path/to/fossil"
17271791
query parameter to "ssh:" URIs if their fossil binary is not in a standard
17281792
place.
1729
- * Add the "[/help?cmd=blame | fossil blame]" command that works just like
1793
+ * Add the "[/help/blame | fossil blame]" command that works just like
17301794
"fossil annotate" but uses a different output format that includes the
17311795
user who made each change and omits line numbers.
17321796
* Add the "Tarball and ZIP-archive Prefix" configuration parameter under
17331797
Admin/Configuration.
17341798
* Fix CGI processing so that it works on web servers that do not
17351799
supply REQUEST_URI.
17361800
* Add options --dirsonly, --emptydirs, and --allckouts to the
1737
- "[/help?cmd=clean | fossil clean]" command.
1801
+ "[/help/clean | fossil clean]" command.
17381802
* Ten-fold performance improvement in large "fossil blame" or
17391803
"fossil annotate" commands.
1740
- * Add option -W|--width and --offset to "[/help?cmd=timeline | fossil timeline]"
1741
- and "[/help?cmd=finfo | fossil finfo]" commands.
1742
- * Option -n|--limit of "[/help?cmd=timeline | fossil timeline]" now
1804
+ * Add option -W|--width and --offset to "[/help/timeline | fossil timeline]"
1805
+ and "[/help/finfo | fossil finfo]" commands.
1806
+ * Option -n|--limit of "[/help/timeline | fossil timeline]" now
17431807
specifies the number of entries, just like all other commands which
17441808
have the -n|--limit option. The various timeline-related functions
17451809
now output "--- ?? limit (??) reached ---" at the end whenever
17461810
appropriate. Use "-n 0" if no limit is desired.
17471811
* Fix handling of password embedded in Fossil URL.
1748
- * New <tt>--once</tt> option to [/help?cmd=clone | fossil clone] command
1812
+ * New <tt>--once</tt> option to [/help/clone | fossil clone] command
17491813
which does not store the URL or password when cloning.
1750
- * Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open
1814
+ * Modify [/help/ui | fossil ui] to respect "default user" in an open
17511815
repository.
17521816
* Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
17531817
* Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
17541818
* Advanced possibilities for commit and ticket change notifications over
17551819
http using TH1 scripting.
17561820
* Add --sha1sum and --integrate options
1757
- to the "[/help?cmd=commit | fossil commit]" command.
1821
+ to the "[/help/commit | fossil commit]" command.
17581822
* Add the "clean" and "extra" subcommands to the
1759
- "[/help?cmd=all | fossil all]" command
1760
- * Add the --whatif option to "[/help?cmd=clean|fossil clean]" that works the
1823
+ "[/help/all | fossil all]" command
1824
+ * Add the --whatif option to "[/help/clean|fossil clean]" that works the
17611825
same as "--dry-run",
17621826
so that the name does not collide with the --dry-run option of "fossil all".
17631827
* Provide a configuration option to show dates on the web timeline
17641828
as "YYMMMDD HH:MM"
17651829
* Add an option to the "stats" webpage that allows an administrator to see
17661830
the current repository schema.
1767
- * Enhancements to the "[/help?cmd=/vdiff|/vdiff]" webpage for more difference
1831
+ * Enhancements to the "[/help/www/vdiff|/vdiff]" webpage for more difference
17681832
display options.
17691833
* Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative
17701834
to "/dir" and make it the default way of showing file lists.
17711835
* Send gzipped HTTP responses to clients that support it.
17721836
17731837
<h2>Changes For Version 1.27 (2013-09-11)</h2>
1774
- * Enhance the [/help?cmd=changes | fossil changes],
1775
- [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
1776
- [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
1838
+ * Enhance the [/help/changes | fossil changes],
1839
+ [/help/clean | fossil clean], [/help/extras | fossil extras],
1840
+ [/help/ls | fossil ls] and [/help/status | fossil status] commands
17771841
to restrict operation to files and directories named on the command-line.
1778
- * New --integrate option to [/help?cmd=merge | fossil merge], which
1842
+ * New --integrate option to [/help/merge | fossil merge], which
17791843
automatically closes the merged branch when committing.
17801844
* Renamed <tt>/stats_report</tt> page to [/reports]. Graph width is now
17811845
relative, not absolute.
17821846
* Added <tt>yw=YYYY-WW</tt> (year-week) filter to timeline to limit the results
17831847
to a specific year and calendar week number, e.g. [/timeline?yw=2013-01].
17841848
* Updates to SQLite to prevent opening a repository file using file descriptors
17851849
1 or 2 on Unix. This fixes a bug under which an assertion failure could
17861850
overwrite part of a repository database file, corrupting it.
17871851
* Added support for unlimited line lengths in side-by-side diffs.
1788
- * New --close option to [/help?cmd=commit | fossil commit], which
1852
+ * New --close option to [/help/commit | fossil commit], which
17891853
immediately closes the branch being committed.
1790
- * Added <tt>chart</tt> option to [/help?cmd=bisect | fossil bisect].
1854
+ * Added <tt>chart</tt> option to [/help/bisect | fossil bisect].
17911855
* Improvements to the "human or bot?" determination.
17921856
* Reports errors about missing CGI-standard environment variables for HTTP
17931857
servers which do not support them.
17941858
* Minor improvements to sync support on Windows.
1795
- * Added <tt>--scgi</tt> option to [/help?cmd=server | fossil server].
1859
+ * Added <tt>--scgi</tt> option to [/help/server | fossil server].
17961860
* Internal improvements to the sync process.
17971861
* The internals of the JSON API are now MIT-licensed, so downstream
17981862
users/packagers are no longer affected by the "do no evil" license
17991863
clause.
18001864
18011865
<h2>Changes For Version 1.26 (2013-06-18)</h2>
1802
- * The argument to the --port option for the [/help?cmd=ui | fossil ui] and
1803
- [/help?cmd=server | fossil server] commands can take an IP address in addition
1866
+ * The argument to the --port option for the [/help/ui | fossil ui] and
1867
+ [/help/server | fossil server] commands can take an IP address in addition
18041868
to the port number, causing Fossil to bind to just that one IP address.
18051869
* After prompting for a password, also ask if that password should be
18061870
remembered.
18071871
* Performance improvements to the diff engine.
18081872
* Fix the side-by-side diff engine to work better with multi-byte Unicode text.
@@ -1809,11 +1873,11 @@
18091873
* Color-coding in the web-based annotation (blame) display. Fix the annotation
18101874
engine so that it is no longer confused by time-warps.
18111875
* The markdown formatter is now available by default and can be used for
18121876
tickets, wiki, and embedded documentation.
18131877
* Add subcommands "fossil bisect log" and "fossil bisect status" to the
1814
- [/help?cmd=bisect | fossil bisect] command, as well as other bisect enhancements.
1878
+ [/help/bisect | fossil bisect] command, as well as other bisect enhancements.
18151879
* Enhanced defenses that prevent spiders from using excessive CPU and bandwidth.
18161880
* Consistent use of the -n or --dry-run command line options.
18171881
* Win32: Fossil now understands Cygwin paths containing one or more of
18181882
the characters <nowiki>"*:<>?|</nowiki>. Those are normally forbidden in
18191883
win32. This means that the win32 fossil.exe is better usable in a Cygwin
18201884
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,44 +1,108 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_27'>Changes for version 2.27 (pending)</h2><ol>
4 <li> Fix a SQL injection on the [/help?cmd=/file|/file page]. Thanks to
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5 additional defenses built into Fossil, as well as good luck, this injection
6 is not exploitable for either data exfiltration or privilege escalation. The
7 only possible result of invoking the injection is a harmless SQL syntax error.
8 (The [https://en.wikipedia.org/wiki/Swiss_cheese_model|holes in the Swiss cheese]
9 did not line up!)
10 <li> Enhance the chng= query parameter on the [/help?cmd=/timeline|timeline page]
11 so that it works with other query parameters like p=, d=, from=, and to=.
12 <li> Always include nodes identify by sel1= and sel2= in the /timeline display.
13 <li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command.
14 <li> Require at least an anonymous login to access the /blame page and similar,
15 to help prevent robots from soaking up excess CPU time on such pages.
 
 
 
 
 
 
 
 
 
 
16 <li> When walking the filesystem looking for Fossil repositories, avoid descending
17 into directories named "/proc".
18 <ll> Reduce memory requirements for sending authenticated sync protocol
19 messages.
20 </ol>
 
 
 
 
21
22 <h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
23 <li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
24 <ol type="a">
25 <li> The argument to the --from option can be a directory name, causing
26 Fossil to use files under that directory as the baseline for the diff.
27 <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
28 is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
29 are available, or a --by diff if not.
30 <li> The "Reload" button is added to --tk diffs, to bring the displayed
31 diff up to date with the latest changes on disk.
32 <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
33 diffs of multiple files.
34 </ol>
35 <li>Added the [/help?cmd=/ckout|/ckout web page] to provide information
36 about pending changes in a working check-out
37 <li>Enhancements to the [/help?cmd=ui|fossil ui] command:
38 <ol type="a">
39 <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
40 start page. Or, if the new "--from PATH" option is present, the
41 default start page becomes "/ckout?exbase=PATH".
42 <li> The new "--extpage FILENAME" option opens the named file as if it
43 where in a [./serverext.wiki|CGI extension]. Example usage: the
44 person editing this change log has
@@ -46,25 +110,25 @@
46 press "Reload" on the web browser to view edits.
47 <li> Accept both IPv4 and IPv6 connections on all platforms, including
48 Windows and OpenBSD. This also applies to the "fossil server"
49 command.
50 </ol>
51 <li>Enhancements to [/help?cmd=merge|fossil merge]:
52 <ol type="a">
53 <li> Added the [/help?cmd=merge-info|fossil merge-info] command and
54 especially the --tk option to that command, to provide analysis
55 of the most recent merge or update operation.
56 <li> When a merge conflict occurs, a new section is added to the conflict
57 text that shows Fossil's suggested resolution to the conflict.
58 </ol>
59 <li>Enhancements to [/help?cmd=commit|fossil commit]:
60 <ol type="a">
61 <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
62 in the check-in comment, it will alert the developer and give
63 him or her the opportunity to edit the comment before continuing.
64 This feature is controllable by the
65 [/help?cmd=verify-comments|verify-comments setting].
66 <li> The new "--if-changes" option causes the commit to become
67 a quiet no-op if there are no pending changes.
68 <li> Added the ability to sign check-ins with SSH keys. Artifacts signed
69 this way are ignored by all previous fossil versions, as if they
70 were plain-text file content instead of Fossil artifacts.
@@ -76,13 +140,13 @@
76 </ol>
77 <li>Deprecate the --comfmtflags and --comment-format global options and
78 no longer list them in the built-in help, but keep them working for
79 backwards compatibility.
80 Alternative TTY comment formatting can still be specified using the
81 [/help?cmd=comment-format|comment-format setting], if desired. The
82 default comment format is now called "canonical", not "legacy".
83 <li>Enhancements to the [/help?cmd=/timeline|/timeline page]:
84 <ol type="a">
85 <li> Added the "ml=" ("Merge-in List") query parameter that works
86 like "rl=" ("Related List") but adds "mionly" style related
87 check-ins instead of the full "rel" style.
88 <li> For "tl=", "rl=", and "ml=", the order of the branches in the
@@ -111,33 +175,33 @@
111 in which case the timeline shows those check-ins that are both
112 ancestors of p= and descendants of d=.
113 <li> The saturation and intensity of user-specified checkin and branch
114 background colors are automatically adjusted to keep the colors
115 compatible with the current skin, unless the
116 [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.
117 </ol>
118 <li>The [/help?cmd=/docfile|/docfile webpage] was added. It works like
119 /doc but keeps the title of markdown documents with the document rather
120 that moving it up to the page title.
121 <li>Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
122 and debugging
123 <li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
124 decoding of the artifact described by NAME.
125 <li>Improvements to the [/help?cmd=patch|fossil patch] command:
126 <ol type="a">
127 <li> Fix a bug in "fossil patch create" that causes
128 [/help?cmd=revert|fossil revert] operations that happened
129 on individualfiles after a [/help?cmd=merge|fossil merge]
130 to be omitted from the patch.
131 <li> Added the [/help?cmd=patch|patch alias] command for managing
132 aliases for remote checkout names.
133 </ol>
134 <li>Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
135 <ol type="a">
136 <li> Add the ability to search the help text, either in the UI
137 (on the [/help?cmd=/search|/search page]) or from the command-line
138 (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
139 <li> Accepts an optional SUBCOMMAND argument following the
140 COMMAND argument and only shows results for the specified
141 subcommand, not the entire command.
142 <li> The -u (--usage) option shows only the command-line syntax
143 <li> The -o (--options) option shows only the command-line options
@@ -153,11 +217,11 @@
153 <li> Link the version field in ticket view to a matching checkin or tag.
154 <li> Show creation time in report and ticket view.
155 <li> Show previous comments in edit ticket as reference.
156 </ol>
157 <li>Added the "hash" query parameter to the
158 [/help?cmd=/whatis|/whatis webpage].
159 <li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
160 which alerts subscribers when an admin creates a new user or
161 when a user's permissions change.
162 <li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
163 the substring "description", then the project description for each repository
@@ -169,44 +233,44 @@
169 <ol type="a">
170 <li> TH1 now makes a distinction between
171 [/doc/trunk/www/th1.md#taint|tainted and untainted string values].
172 This makes it more difficult to write custom TH1 scripts that
173 contain XSS or SQL-injection bugs. The
174 [/help?cmd=vuln-report|vuln-report] setting was added to control
175 what Fossil does when it encounters a potential TH1
176 security problem.
177 <li> The "--th" option was removed from the [/help?cmd=pikchr|fossil pikchr]
178 command.
179 <li> The "enable_htmlify" TH1 command was removed.
180 </ol>
181 <li>Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
182 the frequency of reconnection attempts over time and providing feedback
183 to the user when the connection is down.
184 <li>The [/help?cmd=/sqlar|/sqlar] page does not work for users who are not logged
185 in, nor are links to that page displayed to users who are not logged in. Being
186 logged in as "anonymous" is sufficient to overcome this restriction, assuming
187 that "anonymous" can download tarballs and ZIP archives.
188 <li>Many other minor fixes and additions.
189 </ol>
190
191 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
192
193 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
194 that have non-ASCII filenames
195 * Add the [/help?cmd=tree|fossil tree] command.
196 * On case-insensitive filesystems, store files using the filesystem's
197 preferred case rather than the case typed in by the user.
198 * Change the name "fossil cherry-pick" command to "fossil cherrypick",
199 which is more familiar to Git users. Retain the legacy name for
200 compatibility.
201 * Add new query parameters to the [/help?cmd=/timeline|/timeline page]:
202 d2=, p2=, and dp2=.
203 * Add options to the [/help?cmd=tag|fossil tag] command that will list tag values.
204 * Add the -b|--brief option to the [/help?cmd=status|fossil status] command.
205 * Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page].
206 * Add history search to the [/help?cmd=/chat|/chat page].
207 * Add Unix socket support to the [/help?cmd=server|server command].
208 * On Windows, use the root certificates managed by the operating system
209 (requires OpenSSL 3.2.0 or greater).
210 * Take into account zero-width and double-width unicode characters when
211 formatting the command-line timeline.
212 * Update the built-in SQLite to version 3.47.0. Precompiled binaries are
@@ -243,11 +307,11 @@
243 </ul>
244 * If an "ssh:" sync fails in a way that suggests that the fossil executable
245 could not be found on the remote host, then retry after adding a PATH=
246 prefix to the command. This helps "ssh:" to "just work" when the server
247 is a Mac.
248 * Enhancements to the [/help?cmd=/timeline|/timeline page]:
249 <ul>
250 <li> Add the x= query paramater
251 <li> Add the shortcut tl= and rl= query parameters
252 <li> Add support for from=,ft= and from=,bt= query parameter combinations
253 <li> Automatically highlight the endpoints for from=,to= queries.
@@ -257,16 +321,16 @@
257 * Moved the /museum/repo.fossil file referenced from the Dockerfile from
258 the ENTRYPOINT to the CMD part to allow use of --repolist mode.
259 * The [/uvlist] page now shows the hash algorithm used so that
260 viewers don't have to guess. The hash is shown in a fixed-width
261 font for a more visually pleasing display.
262 * If the [/help?cmd=autosync|autosync setting] contains keyword "all",
263 the automatic sync occurs against all defined remote repositories, not
264 just the default.
265 * Markdown formatter: improved handling of indented fenced code blocks
266 that contain blank lines.
267 * When doing a "[/help?cmd=add|fossil add]" on a system with case-insensitive
268 but case-preserving filenames (Mac and Windows) try to use the filename
269 case as it is known to the filesystem, not the case entered by the
270 user on the command-line. See
271 [forum:/forumpost/30d9c0d131610f53|forum thread 30d9c0d131610f53].
272 * Fix problems with one-click unsubscribe on email notifications.
@@ -280,17 +344,17 @@
280 <h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
281
282 * Add ability to "close" forum threads, such that unprivileged users
283 may no longer respond to them. Only administrators can close
284 threads or respond to them by default, and the
285 [/help?cmd=forum-close-policy|forum-close-policy setting] can be
286 used to add that capability to moderators.
287 * Add the [/help?cmd=all|fossil all whatis] command.
288 * The [/help?cmd=status|fossil status] command and relevant UI pages now
289 correctly report files which were both renamed <b>and</b> edited as such.
290 * Show default value of settings that have a default in
291 [/help?cmd=help|fossil help SETTING] output.
292 * On timeline graphs, show closed check-ins using an X in the middle of the
293 node circle or box.
294 * New options for email notification: Get email only for the first
295 post in each new thread, and/or posts that are in reply to my posts.
296 * Fix a regression bug introduced in version 2.22 that caused FTS5 searches
@@ -303,35 +367,35 @@
303 <li> Better defense against cross-site request forgery (CSRF)
304 attacks.
305 <li> Improvements to static analysis of source code (the codecheck1.c
306 file in the source tree).
307 </ul>
308 * Enhance the [/help?cmd=/dir|treeview file listings]
309 ([/dir?type=tree&ci=trunk|example]) by displaying file sizes
310 and adding the option to sort by file size.
311 * The [/help?cmd=fts-config|fossil fts-config] command now shows how much
312 repository space is used by the full-text index.
313 * Changing a setting to an empty string is now the same as deleting the
314 setting, in most cases. There are a few exceptions, indicated by the
315 keep-empty flag on the setting definition.
316 * The [/help?cmd=branch|fossil branch list] command can now filter branches
317 that have/have not been merged into the current branch.
318 * Improvements to interactions with remote repositories over SSH:
319 <ul>
320 <li> Print the text of the SSH command that is run to do remote interaction,
321 for full disclosure to the operator.
322 <li> Add a PATH= argument to the [/help?cmd=ui|fossil ui remote:/] and
323 [/help?cmd=patch|fossil patch push/pull remote:...] commands so that
324 they work when the "remote" machine is a Mac and the "fossil"
325 executable is in the $HOME/bin directory.
326 </ul>
327 * Update built-in libraries SQLite, ZLib, Pikchr to their latest versions.
328 * Documentation enhancements and typo fixes.
329
330
331 <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2>
332 * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a">
333 <li> Add the ft=TAG query parameter which in combination with d=Y
334 shows all descendants of Y up to TAG
335 <li> Enhance the s=PATTERN (search) query parameter so that forum post
336 text is also searched when the "vfx" query parameter is used
337 <li> Fix the u= (user) query parameter so that it works with a= and b=
@@ -355,56 +419,56 @@
355 searching in Chinese.
356 * Comment lines (starting with a '#') are now supported inside
357 [./settings.wiki#versionable|versioned settings].
358 * Default permissions for anonymous users in new repositories are
359 changed to "hz".
360 * The [/help?cmd=status|fossil status] command now detects when a
361 file used to be a symlink and has been replaced by a regular file.
362 (It previously checked for the inverse case only.)
363 * The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same
364 parser as the *-glob settings instead of its prior idiosyncratic
365 parser, allowing quoted whitespace in patterns.
366 * Enhancements to the [/help?cmd=/reports|/reports webpage]:
367 <ol type="a">
368 <li> The by-week, by-month, and by-year options now show an estimated
369 size of the current week, month, or year as a dashed box.
370 <li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins".
371 </ol>
372
373 <h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2>
374 * Users can request a password reset. This feature is disabled by default.
375 Use the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it.
376 New web pages [/help?cmd=/resetpw|/resetpw] and
377 [/help?cmd=/reqpwreset|/reqpwreset] added.
378 * Add the [/help?cmd=repack|fossil repack] command (together with
379 [/help?cmd=all|fossil all repack]) as a convenient way to optimize the
380 size of one or all of the repositories on a system.
381 * Add the ability to put text descriptions on ticket report formats.
382 * Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
383 * The [/help?cmd=/chat|/chat page] can now embed fossil-rendered
384 views of wiki/markdown/pikchr file attachments with the caveat that such
385 embedding happens in an iframe and thus does not inherit styles and such
386 from the containing browser window.
387 * The [/help?cmd=all|fossil all remote] subcommand added to "fossil all".
388 * Passwords for remembered remote repositories are now stored as irreversible
389 hashes rather than obscured clear-text, for improved security.
390 * Add the "nossl" and "nocompress" options to CGI.
391 * Update search infrastructure from FTS4 to FTS5.
392 * Add the [/help?cmd=/deltachain|/deltachain] page for debugging purposes.
393 * Writes to the database are disabled by default if the HTTP request
394 does not come from the same origin. This enhancement is a defense in depth
395 measure only; it does not address any known vulnerabilities.
396 * Improvements to automatic detection and mitigation of attacks from
397 malicious robots.
398
399 <h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
400 * Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If
401 it is not an empty string, then any changes that would appear on the timeline
402 are announced in [./chat.md|the chat room].
403 * The /unsubscribe page now requests confirmation. [./alerts.md|Email notifications]
404 now contain only an "Unsubscribe" link, and not a link to subscription management.
405 * Added the "[/help?cmd=branch|fossil branch lsh]" subcommand to list the
406 most recently modified branches.
407 * More elements of the /info page are now inside of an accordion.
408 * Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all
409 commands which still used the former name, for consistency.
410 * Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch"
@@ -434,11 +498,11 @@
434 accomplished using a Common Table Expression in the underlying SQL.
435 * Sort tag listings (command line and webpage) by taking numbers into
436 consideration so as to cater for tags that follow semantic versioning.
437 * On the wiki listings, omit by default wiki pages that are associated with
438 check-ins and branches.
439 * Add the new "[/help?cmd=describe|fossil describe]" command.
440 * Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support].
441 See corresponding [../test/markdown-test3.md|test cases],
442 [/wiki?name=branch/markdown-footnotes#il|known limitations] and
443 [forum:/forumthread/ee1f1597e46ec07a|discussion].
444 * Add the new special name "start:BRANCH" to refer to the first check-in of
@@ -450,96 +514,96 @@
450 operation. Also require explicit "system" proxy setting to use
451 "http_proxy" environment variable.
452 * Reimplemented the [/pikchrshow] app to use a WebAssembly build of
453 pikchr so that it can render pikchrs on the client instead of requiring
454 a server round-trip.
455 * Add the [/help?cmd=email-listid|email-listid setting]. If set, it is
456 used as the List-ID header for all outbound notification emails.
457 * Add the "--branch" option to the "[/help?cmd=timeline|timeline]" command
458 to restrict the displayed items to a specific branch.
459 * Add the "--versions" option to "[/help?cmd=diff|fossil diff]"
460 to display details about the compared versions into the patch header.
461 * Numerous other minor enhancements.
462
463 <h2 id='v2_18'>Changes for version 2.18 (2022-02-23)</h2>
464 * Added support for [./ssl-server.md|SSL/TLS server mode] for commands
465 like "[/help?cmd=server|fossil server]" and "[/help?cmd=http|fossil http]"
466 * The new [/help?cmd=cherry-pick|cherry-pick command] is an alias for
467 [/help?cmd=merge|merge --cherrypick].
468 * Add new setting "[/help?cmd=large-file-size|large-file-size]". If the size
469 of any file in a commit exceeds this size, a warning is issued.
470 * Query parameter "year=YYYY" is now accepted by [/help?cmd=/timeline|/timeline].
471 * The [/help?cmd=tar|tar] and [/help?cmd=zip|zip commands] no longer
472 sterilize the manifest file.
473 * Further improvement to diff alignment in cases that involve both
474 edits and indentation changes.
475 * [/doc/trunk/www/chat.md|Chat] improvements:<ul>
476 <li> [/help?cmd=/chat|The /chat page] input options have been reworked
477 again for better cross-browser portability.
478 <li> When sending a [/help?cmd=/chat|/chat] message fails, it is no longer
479 immediately lost and sending may optionally be retried.
480 <li> [/help?cmd=/chat|/chat] can now optionally embed attachments of certain
481 types directly into message bodies via an iframe.
482 <li> Add the "--as FILENAME" option to the "[/help?cmd=chat|fossil chat send]"
483 command.
484 <li> Added the "[/help?cmd=chat|fossil chat pull]" command, available to
485 administrators only, for backing up the chat conversation.
486 </ul>
487 * Promote the test-detach command into the [/help?cmd=detach|detach command].
488 * For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option,
489 if no URL is specified then use the last URL from the most recent prior
490 "fossil pull --from-parent-project".
491 * Add options --project-name and --project-desc to the
492 "[/help?cmd=init|fossil init]" command.
493 * The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment
494 variable for clients.
495 * Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such
496 that it includes the query string. This is how most other systems understand
497 REQUEST_URI.
498 * Added the --transport-command option to [/help?cmd=sync|fossil sync]
499 and similar.
500
501 <h2 id='v2_17'>Changes for version 2.17 (2021-10-09)</h2>
502
503 * Major improvements to the "diff" subsystem, including: <ul>
504 <li> Added new [/help?cmd=diff|formatting options]: --by, -b, --webpage, --json, --tcl.
505 <li> Partial-line matching for unified diffs
506 <li> Better partial-line matching for side-by-side diffs
507 <li> Buttons on web-based diffs to show more context
508 <li> Performance improvements
509 </ul>
510 * The --branchcolor option on [/help?cmd=commit|fossil commit] and
511 [/help?cmd=amend|fossil amend] can now take the value "auto" to
512 force Fossil to use its built-in automatic color choosing algorithm.
513 * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running
514 [/help?cmd=open|fossil open].
515 * Add the [/help?cmd=ticket-default-report|ticket-default-report setting],
516 which if set to the title of a ticket report causes that ticket report
517 to be displayed below the search box in the /ticket page.
518 * The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page
519 causes all graph coloring to be omitted.
520 * Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]"
521 feature so that it works better on a wider variety of platforms.
522 * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for
523 the current page and list URLs suitable for pasting them into the page.
524 * Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
525 and similar.
526 * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
527 the --verbose option.
528 * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
529 </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
530 * The "-p" option to [/help?cmd=branch|fossil branch list] shows only
531 private branches.
532 * The [/md_rules|Markdown formatter] now interprets the content of
533 block HTML markup (such as &lt;table&gt;) in most cases. Only content
534 of &lt;pre&gt; and &lt;script&gt; is passed through verbatim.
535 * The [/help?cmd=wiki|wiki list command] no longer lists "deleted"
536 pages by default. Use the new <tt>--all</tt> option to include deleted
537 pages in the output.
538 * The [/help?cmd=all|fossil all git status] command only shows reports for
539 the subset of repositories that have a configured Git export.
540 * The [/help?cmd=/chat|/chat] configuration was reimplemented and
541 provides new options, including the ability for a repository
542 administrator to
543 [./chat.md#notifications|extend the selection of notification sounds]
544 using unversioned files.
545 * Chat now uses fossil's full complement of markdown features,
@@ -548,14 +612,14 @@
548 markdown-processed when they are sent instead of when they
549 are saved.
550 * Added a chat message preview mode so messages can be previewed
551 before being sent. Similarly, added a per-message ability to view
552 the raw un-parsed message text.
553 * The hotkey to activate preview mode in [/help?cmd=/wikiedit|/wikiedit],
554 [/help?cmd=/fileedit|/fileedit], and [/help?cmd=/pikchrshow|/pikchrshow]
555 was changed from ctrl-enter to shift-enter in order to align with
556 [/help?cmd=/chat|/chat]'s new preview feature and related future
557 changes.
558
559 <h2 id='v2_16'>Changes for Version 2.16 (2021-07-02)</h2>
560 * <b>Security:</b> Fix the client-side TLS so that it verifies that the
561 server hostname matches its certificate.
@@ -562,11 +626,11 @@
562 * The default "ssh" command on Windows is changed to "ssh" instead of the
563 legacy "plink", as ssh is now generally available on Windows systems.
564 Installations that still need to use the legacy "plink" can make that
565 happen by running: '<tt>fossil set ssh-command "plink -ssh" --global</tt>'.
566 * Added the [./patchcmd.md|fossil patch] command.
567 * The [/help?cmd=ui|fossil ui] command is enhanced in multiple ways:<ol>
568 <li> The REPOSITORY argument can be the name of a check-out directory.
569 <li> If the REPOSITORY argument is prefixed by "HOST:" or "USER@HOST:"
570 then the ui is run on the remote machine and tunnelled back to the local
571 machine using ssh. (The latest version of fossil must be installed on
572 both the local and the remote for this to work correctly.)
@@ -575,25 +639,25 @@
575 * The [/brlist|/brlist web page] allows the user to
576 select multiple branches to be displayed together in a single
577 timeline.
578 * The [./forum.wiki|Forum] provides a hyperlink on the author of each
579 post that goes to a timeline of recent posts by that same author.
580 * Added the "[/help?cmd=bisect|fossil bisect run]" command for improved
581 automation of bisects.
582 * The [/help?cmd=merge|fossil merge] command now does a better job merging
583 branches where files have been renamed between the current branch and the
584 branch being merged.
585 * The [/help?cmd=open|fossil open] command allows the repository file
586 to be inside the working directory without requiring the --force flag.
587 * The [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/wikinew|/wikinew]
588 pages now default to markdown format.
589 * The [/help?cmd=/login|/login] page now links to a user's forum post
590 timeline if the repository has forum posts.
591 * Tags may now be propagated for forum posts, wiki pages, and technotes.
592 The [/help?cmd=tag|tag command] can now manipulate and list such tags.
593 * [./caps/login-groups.md|Login-Groups] are now shown on the repository
594 list of the "[/help?cmd=all|fossil all ui]" command.
595 * Administrators can configure [./alerts.md|email alerts] to expire
596 a specific number of days (ex: 365) after the last user contact with
597 the Fossil server. This prevents alert emails being sent to
598 abandoned email accounts forever.
599 * SQL that defines [/tktsetup_tab|database objects for tickets] now
@@ -610,11 +674,11 @@
610 * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
611 the patch is recommended.</b>
612 * The [./defcsp.md|default CSP] has been relaxed slightly to allow
613 images to be loaded from any URL. All other resources are still
614 locked down by default.
615 * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
616 setting to determine the content of the main menu.
617 The ability to edit the
618 "mainmenu" setting is added on the /Admin/Configuration page.
619 * The hamburger menu is now available on most of the built-in skins.
620 * Any built-in skin named "X" can be used instead of the standard
@@ -624,40 +688,40 @@
624 included. The [/skins] page may be used to select a skin.
625 * The [/cookies] page now gives the user an opportunity to delete
626 individual cookies. And the /cookies page is linked from the
627 /sitemap, so that it appears in hamburger menus.
628 * The [/sitemap] extensions are now specified by a single new
629 "[/help?cmd=sitemap-extra|sitemap-extra setting]",
630 rather than a cluster of various
631 "sitemap-*" settings. The older settings are no longer used.
632 <b>This change might require minor server configuration
633 adjustments on servers that use /sitemap extensions.</b>
634 The /Admin/Configuration page provides the ability to edit
635 the new "sitemap-extra" setting.
636 * Added the "--ckout-alias NAME" option to
637 [/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and
638 [/help?cmd=http|fossil http]. This option causes Fossil to
639 understand URIs of the form "/doc/NAME/..." as if they were
640 "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
641 [./embeddeddoc.wiki|embedded documentation] changes prior to
642 check-in.
643 * For diff web pages, if the diff type (unified versus side-by-side)
644 is not specified by a query parameter, and if the
645 "[/help?cmd=preferred-diff-type|preferred-diff-type]"
646 setting is omitted or less than 1, then select the diff type based
647 on a guess of whether or not the request is coming from a mobile
648 device. Mobile gets unified and desktop gets side-by-side.
649 * The various pages which show diffs now have toggles to show/hide
650 individual diffs.
651 * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]"
652 setting to allow an admin to force a default diff type.
653 * The "pikchr-background" setting is now available in
654 "detail.txt" skin files, for better control of Pikchr
655 colors in inverted color schemes.
656 * Add the <tt>--list</tt> option to the
657 [/help?cmd=tarball|tarball],
658 [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
659 commands.
660 * The javascript used to implement the hamburger menu on the
661 default built-in skin has been made generic so that it is usable
662 by a variety of skins, and promoted to an ordinary built-in
663 javascript file.
@@ -665,24 +729,24 @@
665 "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
666 "[/doc/trunk/www/th1.md#capexpr|capexpr]",
667 "foreach", "lappend", and "string match"
668 * The [/help/leaves|leaves command] now shows the branch point
669 of each leaf.
670 * The [/help?cmd=add|fossil add] command refuses to add files whose
671 names are reserved by Windows (ex: "aux") unless the --allow-reserved
672 option is included. This helps prevent Unix users from accidentally
673 creating check-ins that are unreadable by Windows users.
674 * Add the "re=" query parameter to the [/help?cmd=/dir|/dir] webpage,
675 for symmetry with the [/help?cmd=/tree|/tree] page.
676 * Update the built-in SQLite to version 3.35.0.
677 * The ./configure script now has the --print-minimum-sqlite-version option
678 that prints the minimum SQLite version required by the current version
679 of Fossil. This might be used by integrators who insist on building
680 Fossil to link against the system SQLite library rather than the
681 built-in copy of SQLite, to verify that their system SQLite library
682 is recent enough.
683 * Webpage that shows [/help?cmd=/whistory|history of a wiki page]
684 gained client-side UI to help with comparison between two arbitrary
685 versions of a wiki (by the means of anchoring a "baseline" version)
686 and the ability to squeeze several sequential edits made by the same
687 user into a single "recycled" row (the latest edit in that sequence).
688
@@ -702,40 +766,40 @@
702 version 2.14 and then later downgrade or otherwise use an earlier
703 version of Fossil, the email notification mechanism may fail
704 to send out notifications for some events, due to the missing
705 trigger. If you want to
706 permanently downgrade an installation, then you should run
707 "[/help?cmd=rebuild|fossil rebuild]" after the downgrade
708 to get email notifications working again. If you are not using
709 email notification, then the schema change will not affect you in
710 any way.
711 * <b>Schema Update Notice #2:</b>
712 This release changes how the descriptions of wiki edits are stored
713 in the EVENT table, for improved display on timelines. You must
714 run "[/help?cmd=rebuild|fossil rebuild]" to take advantage of
715 this enhancement. Everything will still work without
716 "fossil rebuild", except you will get goofy descriptions of
717 wiki updates in the timeline.
718 * Add support for [./chat.md|Fossil chat].
719 * The "[/help?cmd=clone|fossil clone]" command is enhanced so that
720 if the repository filename is omitted, an appropriate name is derived
721 from the remote URL and the newly cloned repo is opened. This makes
722 the clone command work more like Git, thus making it easier for
723 people transitioning from Git.
724 * Added the --mainbranch option to the [/help?cmd=git|fossil git export]
725 command.
726 * Added the --format option to the
727 "[/help?cmd=timeline|fossil timeline]" command.
728 * Enhance the --numstat option on the
729 "[/help?cmd=diff|fossil diff]" command so that it shows a total
730 number of lines added and deleted and total number of files
731 modified.
732 * Add the "contact" sub-command to [/help?cmd=user|fossil user].
733 * Added commands "[/help?cmd=all|fossil all git export]" and
734 "[/help?cmd=all|fossil all git status]".
735 * Added the "df=CHECKIN" query parameter to the
736 [/help?cmd=/timeline|/timeline page].
737 * Improvements to the "[/sitemap]" page. Add subpages
738 [/sitemap-timeline] and [/sitemap-test].
739 * Better text position in cylinder objects of Pikchr diagrams.
740 * New "details.txt" settings available to custom skins to better control
741 the rendering of Pikchr diagrams:
@@ -757,24 +821,24 @@
757 * Added support for [./interwiki.md|interwiki links].
758 * Enable &lt;del&gt; and &lt;ins&gt; markup in wiki.
759 * Improvements to the Forum threading display.
760 * Added support for embedding [./pikchr.md|pikchr]
761 markup in markdown and fossil-wiki content.
762 * The new "[/help?cmd=pikchr|pikchr]" command can render
763 pikchr scripts, optionally pre-processed with
764 [/doc/trunk/www/th1.md|TH1] blocks and variables exactly like
765 site skins are.
766 * The new [/help?cmd=/pikchrshow|pikchrshow] page provides an
767 editor and previewer for pikchr markup.
768 * In [/help?cmd=/wikiedit|/wikiedit] and
769 [/help?cmd=/fileedit|/fileedit], Ctrl-Enter can now be used
770 initiate a preview and to toggle between the editor and preview
771 tabs.
772 * The <tt>/artifact</tt> and <tt>/file</tt> views, when in
773 line-number mode, now support interactive selection of a range
774 of lines to hyperlink to.
775 * Enhance the [/help?cmd=/finfo|/finfo] webpage so that when query
776 parameters identify both a filename and a checkin, the resulting
777 graph tracks the identified file across renames.
778 * The built-in SQLite is updated to an alpha of version 3.34.0, and
779 the minimum SQLite version is increased to 3.34.0 because the
780 /finfo change in the previous bullet depends on enhancements to
@@ -783,18 +847,18 @@
783 * Countless other minor refinements and documentation improvements.
784
785 <h2 id='v2_12'>Changes for Version 2.12.1 (2020-08-20)</h2>
786
787 * (2.12.1): Fix client-side vulnerabilities discovered by Max Justicz.
788 * Security fix in the "[/help?cmd=git|fossil git export]" command.
789 The same fix is also backported to version 2.10.1 and 2.11.1.
790 New "safety-net" features were added to prevent similar problems
791 in the future.
792 * Enhancements to the graph display for cases when there are
793 many cherry-pick merges into a single check-in.
794 [/timeline?f=2d75e87b760c0a9|Example]
795 * Enhance the [/help?cmd=open|fossil open] command with the new
796 --workdir option and the ability to accept a URL as the repository
797 name, causing the remote repository to be cloned automatically.
798 Do not allow "fossil open" to open in a non-empty working directory
799 unless the --keep option or the new --force option is used.
800 * Enhance the markdown formatter to more closely follow the
@@ -803,65 +867,65 @@
803 Underscores in the middle of identifiers (ex: fossil_printf())
804 no longer need to be escaped.
805 * The markdown-to-html translator can prevent unsafe HTML
806 (for example: &lt;script&gt;) on user-contributed pages like forum and
807 tickets and wiki. The admin can adjust this behavior using
808 the [/help?cmd=safe-html|safe-html setting] on the Admin/Wiki page.
809 The default is to disallow unsafe HTML everywhere.
810 [https://fossil-scm.org/forum/forumpost/3714e6568f|Example].
811 * Added the "collapse" and "expand" capability for long forum posts.
812 [https://fossil-scm.org/forum/forumpost/9297029862|Example]
813 * The "[/help?cmd=remote-url|fossil remote]" command now has options for
814 specifying multiple persistent remotes with symbolic names. Currently
815 only one remote can be used at a time, but that might change in the
816 future.
817 * Add the "Remember me?" checkbox on the login page. Use a session
818 cookie for the login if it is not checked.
819 * Added the experimental "[/help?cmd=hook|fossil hook]" command for
820 managing "hook scripts" that run before checkin or after a push.
821 * Enhance the [/help?cmd=revert|fossil revert] command so that it
822 is able to revert all files beneath a directory.
823 * Add the [/help?cmd=bisect|fossil bisect skip] command.
824 * Add the [/help?cmd=backup|fossil backup] command.
825 * Enhance [/help?cmd=bisect|fossil bisect ui] so that it shows all unchecked
826 check-ins in between the innermost "good" and "bad" check-ins.
827 * Added the <tt>--reset</tt> flag to the "[/help?cmd=add|fossil add]",
828 "[/help?cmd=rm|fossil rm]", and
829 "[/help?cmd=addremove|fossil addremove]" commands.
830 * Added the "<tt>--min</tt> <i>N</i>" and "<tt>--logfile</tt> <i>FILENAME</i>"
831 flags to the [/help?cmd=backoffice|backoffice] command, as well as other
832 enhancements to make the backoffice command a viable replacement for
833 automatic backoffice. Other incremental backoffice improvements.
834 * Added the [/help?cmd=/fileedit|/fileedit page], which allows
835 editing of text files online. Requires explicit activation by
836 a setup user.
837 * Translate built-in help text into HTML for display on web pages.
838 [/help?cmd=help|Example].
839 * On the [/help?cmd=/timeline|/timeline] webpage, the combination
840 of query parameters "p=CHECKIN" and "bt=ANCESTOR" draws all
841 ancestors of CHECKIN going back to ANCESTOR. For example,
842 [/timeline?p=202006271506&bt=version-2.11] shows all ancestors
843 of the checkin that occurred on 2020-06-27 15:06 going back to
844 the 2.11 release.
845 * Update the built-in SQLite so that the
846 "[/help?cmd=sql|fossil sql]" command supports new output
847 modes ".mode box" and ".mode json".
848 * Add the "<tt>obscure()</tt>" SQL function to the
849 "[/help?cmd=sql|fossil sql]" command.
850 * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
851 the "[/help?cmd=sql|fossil sql]" command, providing access to the
852 dispatch table including all help text, and the builtin data files,
853 respectively.
854 * [./delta_format.wiki|Delta compression] is now applied to forum edits.
855 * The [/help?cmd=/wikiedit|wiki editor] has been modernized and is
856 now Ajax-based. The WYSIWYG editing option for Fossil-format wiki
857 pages was removed. (Please let us know, via the site's Forum menu,
858 if that removal unduly impacts you.) This also changes the semantics
859 of the wiki "Sandbox": that pseudo-page may be freely edited but
860 no longer saved via the UI (the [/help?cmd=wiki|wiki CLI command]
861 can, though).
862 * The [/help?cmd=allow-symlinks|allow-symlinks setting] no longer
863 syncs. It must be activated individually on any clones which require
864 symlinks.
865 * Countless documentation enhancements.
866
867 <h2 id='v2_11'>Changes for Version 2.11 (2020-05-25)</h2>
@@ -873,46 +937,46 @@
873 can now omit punctation. So, for example, "202004181942" and
874 "2020-04-18 19:42" mean the same thing.
875 * Enhance backlink processing so that it works with Markdown-formatted
876 tickets and so that it works for wiki pages.
877 Ticket [a3572c6a5b47cd5a].
878 <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to
879 take full advantage of this fix. Fossil will continue
880 to work without the rebuild, but the new backlinks will be missing.</ul>
881 * The algorithm for finding the
882 [./tech_overview.wiki#configloc|location of the configuration database]
883 is enhanced to be XDG-compliant.
884 * Add a hide/show feature to
885 [./wikitheory.wiki#assocwiki|associated wiki] display on
886 check-in and branch information pages.
887 * Enhance the "[/help?cmd=info|fossil info]" command so that it
888 works with no arguments even if not within an open check-out.
889 * Many improvements to the forum and especially email notification
890 of forum posts, in response to community feedback after switching
891 SQLite support from a mailing list over to the forum.
892 * Minimum length of a self-registered user ID increased from 3 to 6
893 characters.
894 * When the "vfx" query parameter is used on the
895 "[/help?cmd=/timeline|/timeline]" page, it causes the complete
896 text of forum posts to be displayed.
897 * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful.
898 * Expose the [/help?cmd=redirect-to-https|redirect-to-https]
899 setting to the [/help?cmd=settings|settings] command.
900 * Improve support for CGI on IIS web servers.
901 * The [./serverext.wiki|/ext page] can now render index files,
902 in the same way as the embedded docs.
903 * Most commands now support the Unix-conventional "<tt>--</tt>"
904 flag to treat all following arguments as filenames
905 instead of flags.
906 * Added the [/help?cmd=mimetypes|mimetypes config setting]
907 (versionable) to enable mimetype overrides and custom definitions.
908 * Add an option on the /Admin/Timeline setup page to set a default
909 timeline style other than "Modern".
910 * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
911 of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
912 into the check-in hash for the document currently being viewed.
913 * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all
914 phantom artifacts.
915 * Enhancements to phantom processing to try to reduce
916 bandwidth-using chatter about phantoms on the sync protocol.
917 * Security: Fossil now assumes that the schema of every
918 database it opens has been tampered with by an adversary and takes
@@ -920,23 +984,23 @@
920 * Security: Fossil now puts the Content-Security-Policy in the
921 HTTP reply header, in addition to also leaving it in the
922 HTML &lt;head&gt; section, so that it is always available, even
923 if a custom skin overrides the HTML &lt;head&gt; and omits
924 the CSP in the process.
925 * Output of the [/help?cmd=diff|fossil diff -y] command automatically
926 adjusts according to the terminal width.
927 * The Content-Security-Policy is now set using the
928 [/help?cmd=default-csp|default-csp setting].
929 * Merge conflicts caused via the [/help?cmd=merge|merge] and
930 [/help?cmd=update|update] commands no longer leave temporary
931 files behind unless the new <tt>--keep-merge-files</tt> flag
932 is used.
933 * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible
934 to all users if the new "artifact_stats_enable" setting is turned
935 on. There is a new checkbox under the /Admin/Access menu to turn
936 that capability on and off.
937 * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing
938 the TLS configuration and the list of SSL Cert exceptions.
939 * Captchas all include a button to read the captcha using an audio
940 file, so that they can be completed by the visually impaired.
941 * Stop using the IP address as part of the login cookie.
942 * Bug fix: fix the SSL cert validation logic so that if an exception
@@ -957,27 +1021,27 @@
957 <h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2>
958
959 * (2.10.2): backport security fixes from 2.12.1
960 * (2.10.1): backport security fix for the "fossil git export" command.
961 * Added support for [./serverext.wiki|CGI-based Server Extensions].
962 * Added the [/help?cmd=repolist-skin|repolist-skin] setting used to
963 add style to repository list pages.
964 * Enhance the hierarchical display of Forum threads to do less
965 indentation and to provide links back to the previous message
966 in the thread. Provide sequential numbers for all messages in
967 a forum thread.
968 * Add support for fenced code blocks and improved hyperlink
969 processing to the [/md_rules|markdown formatter].
970 * Add support for hyperlinks to wiki pages in the
971 [/md_rules|markdown formatter].
972 * Enhance the [/help?cmd=/stat|/stat] page so that it gives the
973 option to show a breakdown of forum posts.
974 * The special check-in name "merge-in:BRANCH" means the source of
975 the most recent merge-in from the parent branch of BRANCH.
976 * Add hyperlinks to branch-diffs on the /info page and from
977 timelines of a branch.
978 * Add graphical context on the [/help?cmd=/vdiff|/vdiff] page.
979 * Uppercase query parameters, POST parameters, and cookie names are
980 converted to all lowercase and entered into the parameter set,
981 instead of being discarded.
982 * Change the default [./hashpolicy.wiki|hash policy] to SHA3.
983 * Timeout [./server/any/cgi.md|CGI requests] after 300 seconds, or
@@ -990,14 +1054,14 @@
990 * Performance optimizations.
991 * Many documentation improvements.
992
993 <h2 id='v2_9'>Changes for Version 2.9 (2019-07-13)</h2>
994
995 * Added the [/help?cmd=git|fossil git export] command and instructions
996 for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project].
997 * Improved handling of relative hyperlinks on the
998 [/help?cmd=/artifact|/artifact] pages for wiki. For example,
999 hyperlinks and the lizard &lt;img&gt; now work correctly
1000 for both [/artifact/2ff24ab0887cf522] and
1001 [/doc/0d7ac90d575004c2415/www/index.wiki].
1002 * Enhancements to the timeline graph layout, to show more information
1003 with less clutter.
@@ -1006,28 +1070,28 @@
1006 configuration.
1007 * Copy buttons added to various check-in hash and branch name links.
1008 * Double-clicking on a /timeline graph node now jumps to the /info page
1009 for the check-in. So, repurpose the timestamp hyperlink to show all
1010 activity around that check-in in time.
1011 * Added the [/help?cmd=touch|fossil touch] command, and the --setmtime
1012 option on the [/help?cmd=open|fossil open] and
1013 [/help?cmd=update|fossil update] commands.
1014 * Many documentation enhancements.
1015 * For the "[/help?cmd=update|fossil update]" and
1016 "[/help?cmd=checkout|fossil checkout]" commands, if a
1017 managed file is removed because it is no longer part of the target
1018 check-in and the directory containing the file is empty after the
1019 file is removed and the directory is not the current working
1020 directory and is not on the [/help?cmd=empty-dirs|empty-dirs]
1021 list, then also remove the directory.
1022 * Update internal Unicode character tables, used in regular expression
1023 handling, from version 11.0 to 12.1.
1024 * In "[/help?cmd=regexp|fossil regexp]", "[/help?cmd=grep|fossil grep]"
1025 and the TH1 "regexp" command, the -nocase option now removes multiple
1026 diacritics from the same character (derived from SQLite's
1027 remove_diacritics=2)
1028 * Added the [/help?cmd=/secureraw|/secureraw] page that requires the
1029 complete SHA1 or SHA3 hash, not just a prefix, before it will deliver
1030 content.
1031 * Accept purely numeric ISO8601 date/time strings as long as they
1032 do not conflict with a hash. Example: "20190510134217" instead of
1033 "2019-05-10 13:42:17". This helps keep URLs shorter and less
@@ -1040,19 +1104,19 @@
1040 extra path element is not needed.
1041 * If an automatic sync gets a permanent redirect request, then update
1042 the saved remote URL to the new address.
1043 * Temporary filenames (for example used for external "diff" commands)
1044 try to preserve the suffix of the original file.
1045 * Added the [/help?cmd=/thisdayinhistory|/thisdayinhistory] web page.
1046 * Enhanced parsing of [/help?cmd=/timeline|/timeline] query parameters
1047 "ymd=", "ym=", and "yw=". All arguments are option (in which case they
1048 default to the current time) and all accept ISO8601 date/times without
1049 punctuation.
1050 * Automatically disapprove pending moderation requests for a user when
1051 that user is deleted. This helps in dealing with spam-bots.
1052 * Improvements to the "Capability Summary" section in the
1053 [/help?cmd=/secaudit0|Security Audit] web-page.
1054 * Use new "ci-lock" and "ci-lock-failed" pragmas in the
1055 [./sync.wiki|sync protocol] to try to prevent accident forks
1056 caused by concurrent commits when operating in auto-sync mode.
1057 * Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details])
1058 that can cause repository databases to be overwritten with debugging
@@ -1153,11 +1217,11 @@
1153 included in the header or footer.
1154 * Add the [./backoffice.md|backoffice].
1155 * Update internal Unicode character tables, used in regular expression
1156 handling, from version 10.0 to 11.0.
1157 * Improvements to the "Security Audit" administration page
1158 * Add the [/help?cmd=branch|fossil branch current] command.
1159 * Add the [./grep.md|grep] command.
1160 * Update the built-in SQLite to version 3.25.1.
1161 * Some code and interfaces are in place to support sending and
1162 receiving email directly via SMTP, but this feature is not yet
1163 complete or ready for production use.
@@ -1176,20 +1240,20 @@
1176 same as "Verbose" in the previous release. The "Verbose" mode is
1177 now like "Compact" except the extra check-in details are shown by
1178 default.
1179 * Add support for ETags:, Last-Modified:, and If-Modified-Since:
1180 cache control mechanisms.
1181 * Enhance the [/help?cmd=/tarball|/tarball],
1182 [/help?cmd=/zip|/zip], and
1183 [/help?cmd=/sqlar|/sqlar] pages so that the checkin
1184 name to be downloaded can be expressed as part of the URI,
1185 and without the need for query parameters.
1186 * On the [/help?cmd=/timeline|/timeline] webpage, add the days=N
1187 query parameter and enhance the ymd=DATE and yw=DATE query parameters
1188 to accept 'now' as an argument to show the latest day or week.
1189 * In the web page that comes up in response to the
1190 [/help?cmd=all|fossil all ui] command, show the last modification
1191 time for each repository, and allow click-to-sort on the modification
1192 time column.
1193 * In the tarball cache replacement algorithm, give extra weight to
1194 tarballs that have been accessed more than once.
1195 * Additional defenses against web-based attacks. There have not been
@@ -1236,35 +1300,35 @@
1236 to define their own URLs on the web interface that are rewritten to
1237 built-in URLs with specific parameters. Create and configure URL Aliases
1238 using the /Setup/URL_Aliases menu option in the web interface.
1239 * Add tech-note search capability.
1240 * Add the -r|--revision and -o|--origin options to the
1241 [/help?cmd=annotate|annotate] command.
1242 * Add the origin= query parameter to the [/help?cmd=/annotate|/annotate]
1243 webpage.
1244 * The [/help?cmd=annotate|fossil annotate] command and the
1245 [/help?cmd=/annotate|/annotate] web page go backwards in time as far
1246 as can be computed in 30 milliseconds by default, rather than stopping
1247 after 20 steps. The new limit= query parameter or the --limit command-line
1248 option can be used to alter this timeout.
1249 * Provide separate [/help#settings|on-line help screens for each setting].
1250 * Back out support for the --no-dir-symlinks option
1251 * Remove support from the legacy configuration sync protocol. The only
1252 way now to do a configuration push or pull is to use the new protocol that
1253 was added in 2011.
1254 * Add the from= and to= query parameters to [/help?cmd=/fdiff|/fdiff]
1255 in order to get a diff of two files in the same check-in.
1256 * Fix the "ssh://" protocol to prevent an attack whereby the attacker convinces
1257 a victim to run a "clone" with a dodgy URL and thereby gains access to their
1258 system.
1259 * Provide a checkbox that will temporarily disable all ad-units.
1260 * Improvements to the [/help?cmd=/stat|/stat] page
1261 * Various new hyperlinks to the [/help?cmd=/bloblist|/bloblist]
1262 and [/help?cmd=/bigbloblist|/bigbloblist] pages.
1263 * Correct the [/help?cmd=/doc|/doc] page to support read-only repositories.
1264 * Correct [/help?cmd=/zip|/zip], [/help?cmd=/tarball|/tarball],
1265 [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to
1266 honor the versioned manifest setting when outside of an open checkout
1267 directory.
1268 * The admin-log and access-log settings are now on by default for
1269 new repositories.
1270 * Update the built-in SQLite to version 3.21.0.
@@ -1272,34 +1336,34 @@
1272 <h2 id='v2_3'>Changes for Version 2.3 (2017-07-21)</h2>
1273
1274 * Update the built-in SQLite to version 3.20.0 (beta).
1275 * Update internal Unicode character tables, used in regular expression
1276 handling, from version 9.0 to 10.0.
1277 * Show the last-sync-URL on the [/help?cmd=/urllist|/urllist] page.
1278 * Added the "Event Summary" activity report.
1279 [/reports?type=ci&view=lastchng|example]
1280 * Added the "Security Audit" page, available to administrators only
1281 * Added the Last Login time to the user list page, for administrators only
1282 * Added the --numstat option to the [/help?cmd=diff|fossil diff] command
1283 * Limit the size of the heap and stack on unix systems, as a proactive
1284 defense against the
1285 [https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt|Stack Clash]
1286 attack.
1287 * Fix "database locked" warnings caused by "PRAGMA optimize".
1288 * Fix a potential XSS vulnerability on the
1289 [/help?cmd=/help|/help] webpage.
1290 * Documentation updates
1291
1292 <h2 id='v2_2'>Changes for Version 2.2 (2017-04-11)</h2>
1293
1294 * GIT comment tags are now handled by Fossil during import/export.
1295 * Show the content of README files on directory listings.
1296 ([/file/skins|example])
1297 * Support for Basic Authentication if enabled (default off).
1298 * Show the hash algorithms used on the
1299 [/help?cmd=/rcvfromlist|/rcvfromlist] page.
1300 * The [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] pages
1301 now use the the r= query parameter
1302 to select which check-in to deliver. The uuid= query parameter
1303 is still accepted for backwards compatibility.
1304 * Update the built-in SQLite to version 3.18.0.
1305 * Run "[https://www.sqlite.org/pragma.html#pragma_optimize|PRAGMA optimize]"
@@ -1308,12 +1372,12 @@
1308 <h2 id='v2_1'>Changes for Version 2.1 (2017-03-10)</h2>
1309
1310 * Add support for [./hashpolicy.wiki|hash policies] that control which
1311 of the Hardened-SHA1 or SHA3-256 algorithms is used to name new
1312 artifacts.
1313 * Add the "gshow" and "gcat" subcommands to [/help?cmd=stash|fossil stash].
1314 * Add the [/help?cmd=/juvlist|/juvlist] web page and use it to construct
1315 the [/uv/download.html|Download Page] of the Fossil self-hosting website
1316 using Ajax.
1317
1318 <h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2>
1319
@@ -1321,23 +1385,23 @@
1321 [https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
1322 implementation by Marc Stevens and Dan Shumow.
1323 * Add the ability to read and understand
1324 [./fileformat.wiki#names|artifact names] that are based on SHA3-256
1325 rather than SHA1, but do not actually generate any such names.
1326 * Added the [/help?cmd=sha3sum|sha3sum] command.
1327 * Update the built-in SQLite to version 3.17.0.
1328
1329 <h2 id='v1_37'>Changes for Version 1.37 (2017-01-16)</h2>
1330
1331 * Add checkbox widgets to various web pages. See [/technote/8d18bf27e9|
1332 this technote] for more information. To get the checkboxes to look as
1333 intended, you must update the CSS in your repository and all clones.
1334 * Add the [/help/all|fossil all ui] command
1335 * Add the [/help?cmd=/file|/file] webpage
1336 * Enhance the [/help?cmd=/brlist|/brlist] webpage to make use of branch colors.
1337 * Add support for the ms=EXACT|LIKE|GLOB|REGEXP query parameter on the
1338 [/help?cmd=/timeline|/timeline] webpage, with associated form widgets.
1339 * Enhance the [/help/changes|changes] and [/help/status|status] commands
1340 with many new filter options so that specific kinds of changes can be
1341 found without having to pipe through grep or sed.
1342 * Enhanced the [/help/sqlite3|fossil sql] command so that it opens the
1343 [./tech_overview.wiki#localdb|checkout database] and the
@@ -1350,11 +1414,11 @@
1350 </ul>
1351 * Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep
1352 crnl-glob as a compatibility alias.
1353 * Added the --command option to the [/help/diff|diff] command.
1354 * Fix a C99-ism that prevents the 1.36 release from building with MSVC.
1355 * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
1356 from the "ticketchng" table.
1357 * Remove the "fusefs" command from builds that do not have the underlying
1358 support enabled.
1359 * Fixes for incremental git import/export.
1360 * Minor security enhancements to
@@ -1364,58 +1428,58 @@
1364
1365
1366 <h2 id='v1_36'>Changes for Version 1.36 (2016-10-24)</h2>
1367
1368 * Add support for [./unvers.wiki|unversioned content],
1369 the [/help?cmd=unversioned|fossil unversioned] command and the
1370 [/help?cmd=/uv|/uv] and [/uvlist] web pages.
1371 * The [/uv/download.html|download page] is moved into
1372 [./unvers.wiki|unversioned content] so that the self-hosting Fossil
1373 websites no longer uses any external content.
1374 * Added the "Search" button to the graphical diff generated by
1375 the --tk option on the [/help?cmd=diff|diff] command.
1376 * Added the "--checkin VERSION" option to the
1377 [/help?cmd=diff|diff] command.
1378 * Various performance enhancements to the [/help?cmd=diff|diff] command.
1379 * Update internal Unicode character tables, used in regular expression
1380 handling, from version 8.0 to 9.0.
1381 * Update the built-in SQLite to version 3.15. Fossil now requires
1382 the SQLITE_DBCONFIG_MAINDBNAME interface of SQLite which is only available
1383 in SQLite version 3.15 and later and so Fossil will not work with
1384 earlier SQLite versions.
1385 * Fix [https://www.mail-archive.com/[email protected]/msg23618.html|multi-line timeline bug]
1386 * Enhance the [/help?cmd=purge|fossil purge] command.
1387 * New command [/help?cmd=shell|fossil shell].
1388 * SQL parameters whose names are all lower-case in Ticket Report SQL
1389 queries are filled in using HTTP query parameter values.
1390 * Added support for [./childprojects.wiki|child projects] that are
1391 able to pull from their parent but not push.
1392 * Added the -nocomplain option to the TH1 "query" command.
1393 * Added support for the chng=GLOBLIST query parameter on the
1394 [/help?cmd=/timeline|/timeline] webpage.
1395
1396 <h2 id='v1_35'>Changes for Version 1.35 (2016-06-14)</h2>
1397
1398 * Enable symlinks by default on all non-Windows platforms.
1399 * Enhance the [/md_rules|Markdown formatting] so that hyperlinks that begin
1400 with "/" are relative to the root of the Fossil repository.
1401 * Rework the [/help?cmd=/setup_ulist|/setup_list page] (the User List page)
1402 to display all users in a click-to-sort table.
1403 * Fix backslash-octal escape on filenames while importing from git
1404 * When markdown documents begin with &lt;h1&gt; HTML elements, use that
1405 header at the document title.
1406 * Added the [/help?cmd=/bigbloblist|/bigbloblist page].
1407 * Enhance the [/help?cmd=/finfo|/finfo page] so that when it is showing
1408 the ancestors of a particular file version, it only shows direct
1409 ancestors and omits changes on branches, thus making it show the same set
1410 of ancestors that are used for [/help?cmd=/blame|/blame].
1411 * Added the --page option to the [/help?cmd=ui|fossil ui] command
1412 * Added the [/help?cmd=bisect|fossil bisect ui] command
1413 * Enhanced the [/help?cmd=diff|fossil diff] command so that it accepts
1414 directory names as arguments and computes diffs on all files contained
1415 within those directories.
1416 * Fix the [/help?cmd=add|fossil add] command so that it shows "SKIP" for
1417 files added that were already under management.
1418 * TH1 enhancements:
1419 <ul><li>Add <nowiki>[array exists]</nowiki> command.</li>
1420 <li>Add minimal <nowiki>[array names]</nowiki> command.</li>
1421 <li>Add tcl_platform(engine) and tcl_platform(platform) array
@@ -1423,32 +1487,32 @@
1423 </ul>
1424 * Get autosetup working with MinGW.
1425 * Fix autosetup detection of zlib in the source tree.
1426 * Added autosetup detection of OpenSSL when it may be present under the
1427 "compat" subdirectory of the source tree.
1428 * Added the [/help?cmd=reparent|fossil reparent] command
1429 * Added --include and --exclude options to [/help?cmd=tarball|fossil tarball]
1430 and [/help?cmd=zip|fossil zip] and the in= and ex= query parameters to the
1431 [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] web pages.
1432 * Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
1433 * If the FOSSIL_PWREADER environment variable is set, then use the program it
1434 names in place of getpass() to read passwords and passphrases
1435 * Option --baseurl now works on Windows.
1436 * Numerous documentation improvements.
1437 * Update the built-in SQLite to version 3.13.0.
1438
1439 <h2 id='v1_34'>Changes for Version 1.34 (2015-11-02)</h2>
1440
1441 * Make the [/help?cmd=clean|fossil clean] command undoable for files less
1442 than 10MiB.
1443 * Update internal Unicode character tables, used in regular expression
1444 handling, from version 7.0 to 8.0.
1445 * Add the new [/help?cmd=amend|amend] command which is used to modify
1446 tags of a "check-in".
1447 * Fix bug in [/help?cmd=import|import] command, handling version 3 of
1448 the svndump format for subversion.
1449 * Add the [/help?cmd=all|all cache] command.
1450 * TH1 enhancements:
1451 <ul><li>Add minimal <nowiki>[lsearch]</nowiki> command. Only exact
1452 case-sensitive matching is supported.</li>
1453 <li>Add the <nowiki>[glob_match]</nowiki>, <nowiki>[markdown]</nowiki>,
1454 <nowiki>[dir]</nowiki>, and <nowiki>[encode64]</nowiki> commands.</li>
@@ -1455,106 +1519,106 @@
1455 <li>Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> commands to
1456 the Tcl integration subsystem.</li>
1457 <li>Add 'double', 'integer', and 'list' classes to the
1458 <nowiki>[string is]</nowiki> command.</li>
1459 </ul>
1460 * Add the --undo option to the [/help?cmd=diff|diff] command.
1461 * Build-in Antirez's "linenoise" command-line editing library for use with
1462 the [/help?cmd=sqlite3|fossil sql] command on Unix platforms.
1463 * Add [/help?cmd=stash|stash cat] as an alias for the
1464 [/help?cmd=stash|stash show] command.
1465 * Automatically pull before [/help?cmd=merge|fossil merge] when auto-sync
1466 is enabled.
1467 * Fix --hard option to [/help?cmd=mv|fossil mv] and [/help?cmd=rm|fossil rm]
1468 to enable them to work properly with certain relative paths.
1469 * Change the mimetype for ".n" and ".man" files to text/plain.
1470 * Display improvements in the [/help?cmd=bisect|fossil bisect chart] command.
1471 * Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5
1472 support (both currently unused within Fossil).
1473
1474 <h2 id='v1_33'>Changes for Version 1.33 (2015-05-23)</h2>
1475 * Improved fork detection on [/help?cmd=update|fossil update],
1476 [/help?cmd=status|fossil status] and related commands.
1477 * Change the default skin to what used to be called "San Francisco Modern".
1478 * Add the [/repo-tabsize] web page
1479 * Add [/help?cmd=import|fossil import --svn], for importing a subversion
1480 repository into fossil which was exported using "svnadmin dump".
1481 * Add the "--compress-only" option to [/help?cmd=rebuild|fossil rebuild].
1482 * Use a pie chart on the [/reports?view=byuser] page.
1483 * Enhanced [/help?cmd=clean|fossil clean --verily] so that it ignores
1484 keep-glob and ignore-glob settings. Added the -x alias for --verily.
1485 * Add the --soft and --hard options to [/help?cmd=rm|fossil rm] and
1486 [/help?cmd=mv|fossil mv]. The default is still --soft, but that is
1487 now configurable at compile-time or by the mv-rm-files setting.
1488 * Improved ability to [./customgraph.md|customize the timeline graph].
1489 * Improvements to the [/sitemap] page.
1490 * Automatically adjust the [/help?cmd=timeline|CLI timeline] to the terminal
1491 width on Linux.
1492 * Added <nowiki>[info commands] and [info vars]</nowiki> commands to TH1.
1493 These commands perform the same function as their Tcl counterparts,
1494 except they do not accept a pattern argument.
1495 * Fix some obscure issues with TH1 expression processing.
1496 * Fix titles in search results for documents that are not wiki, markdown,
1497 or HTML.
1498 * Formally translate TH1 to Tcl return codes and vice-versa, where
1499 necessary, in the Tcl integration subsystem.
1500 * Add [/help?cmd=leaves|fossil leaves -multiple], for finding multiple
1501 leaves on the same branch.
1502 * Added the "Blitz" skin option.
1503 * Removed the ".fossil-settings/keep-glob" file. It should not have been
1504 checked into the repository.
1505 * Update the built-in SQLite to version 3.8.10.2.
1506 * Make [/help?cmd=open|fossil open] honor ".fossil-settings/allow-symlinks".
1507 * Allow [/help?cmd=add|fossil add] to be used on symlinks to nonexistent or
1508 unreadable files in the same way as [/help?cmd=addremove|fossil addremove].
1509 * Added fork warning to be issued if sync produced a fork
1510 * Update the [/help?cmd=/info|info] page to report when a file becomes a
1511 symlink. Additionally show the UUID for files whose types have changed
1512 without changing contents or symlink target.
1513 * Have [/help?cmd=changes|fossil changes] and
1514 [/help?cmd=status|fossil status] report when executable or symlink status
1515 changes on otherwise unmodified files.
1516 * Permit filtering weekday and file [/help?cmd=/reports|reports] by user.
1517 Also ensure the user parameter is preserved when changing types. Add a
1518 field for direct entry of the user name to each applicable report.
1519 * Create parent directories of [/help?cmd=settings|empty-dirs] if they don't
1520 already exist.
1521 * Inhibit timeline links to wiki pages that have been deleted.
1522
1523 <h2 id='v1_33'>Changes for Version 1.32 (2015-03-14)</h2>
1524 * When creating a new repository using [/help?cmd=init|fossil init], ensure
1525 that the new repository is fully compatible with historical versions of
1526 Fossil by having a valid manifest as RID 1.
1527 * Anti-aliased rendering of arrowheads on timeline graphs.
1528 * Added vi/less-style key bindings to the --tk diff GUI.
1529 * Documentation updates to fix spellings and changes all "checkins" to
1530 "check-ins".
1531 * Add the --repolist option to server commands such as
1532 [/help?cmd=server|fossil server] or [/help?cmd=http|fossil http].
1533 * Added the "Xekri" skin.
1534 * Enhance the "ln=" query parameter on artifact displays to accept multiple
1535 ranges, separate by spaces (or "+" when URL-encoded).
1536 * Added [/help?cmd=forget|fossil forget] as an alias for
1537 [/help?cmd=rm|fossil rm].
1538
1539 <h2 id='v1_31'>Changes For Version 1.31 (2015-02-23)</h2>
1540 * Change the auxiliary schema by adding columns MLINK.ISAUX and MLINK.PMID
1541 columns to the schema, to support better drawing of file change graphs.
1542 A [/help?cmd=rebuild|fossil rebuild] is recommended but is not required.
1543 so that the new graph drawing logic can work effectively.
1544 * Added [/search|search] over Check-in comments, Documents, Tickets and
1545 Wiki. Disabled by default. The search can be either a full-scan or it
1546 can use an index that is kept up-to-date automatically. The new
1547 /srchsetup web-page and the [/help?cmd=fts-config|fts-config] command
1548 were added to help configure the search capability. Expect further
1549 enhancements to the search capabilities in subsequent releases.
1550 * Added form elements to some submenus (in particular the /timeline)
1551 for easier operation.
1552 * Added the --ifneeded option to [/help?cmd=rebuild|fossil rebuild].
1553 * Added "override skins" using the "skin:" line of the CGI script or
1554 using the --skin LABEL option on the [/help?cmd=server|server],
1555 [/help?cmd=ui|ui], or [/help?cmd=http|http] commands.
1556 * Embedded html documents that begin with
1557 &lt;doc class="fossil-doc"&gt; are displayed with standard
1558 headers and footers added.
1559 * Allow &lt;div style='...'&gt; markup in [/wiki_rules|wiki].
1560 * Renamed "Events" to "Technical Notes", while updating the technote
@@ -1561,24 +1625,24 @@
1561 display and control pages. Add support for technotes as plain text
1562 or as Markdown.
1563 * Added the [/md_rules] pages containing summary instructions on the
1564 Markdown format.
1565 * Added the --repolist and --nojail options to the various server commands
1566 (ex: [/help?cmd=server|fossil server]).
1567 * Added the [/help?cmd=all|fossil all add] subcommand to "fossil all".
1568 * Improvements to the /login page. Some hyperlinks to pages that require
1569 "anonymous" privileges are displayed even if the current user is "nobody"
1570 but automatically redirect to /login.
1571 * The [/help?cmd=/doc|/doc] web-page will now try to deliver the file
1572 "404.md" from the top-level directory (if such a file exists) in
1573 place of its built-in 404 text.
1574 * Download of Tarballs and ZIP Archives by user "nobody" is now enabled
1575 by default in new repositories.
1576 * Enhancements to the table sorting controls. More display tables
1577 are now sortable.
1578 * Add IPv6 support to [/help?cmd=sync|fossil sync] and
1579 [/help?cmd=clone|fossil clone]
1580 * Add more skins such as "San Francisco Modern" and "Eagle".
1581 * During shutdown, check to see if the check-out database (".fslckout")
1582 contains a lot of free space, and if it does, VACUUM it.
1583 * Added the [/mimetype_list] page.
1584 * Added the [/hash-collisions] page.
@@ -1586,14 +1650,14 @@
1586 ticket reports.
1587 * Break out the components (css, footer, and header) for the
1588 various built-in skins into separate files in the source tree.
1589
1590 <h2 id='v1_30'>Changes For Version 1.30 (2015-01-19)</h2>
1591 * Added the [/help?cmd=bundle|fossil bundle] command.
1592 * Added the [/help?cmd=purge|fossil purge] command.
1593 * Added the [/help?cmd=publish|fossil publish] command.
1594 * Added the [/help?cmd=unpublished|fossil unpublished] command.
1595 * Enhance the [/tree] webpage to show the age of each file with the option
1596 to sort by age.
1597 * Enhance the [/brlist] webpage to show additional information about each branch
1598 and to be sortable by clicking on column headers.
1599 * Add support for Docker. Just install docker and type
@@ -1603,17 +1667,17 @@
1603 copies of all historical check-ins. This only works on systems that
1604 support FuseFS.
1605 * Add the administrative log that records all configuration.
1606 * Added the [/sitemap] webpage.
1607 * Added the [/bloblist] web page.
1608 * Let [/help?cmd=new|fossil new] no longer create an initial empty commit
1609 by default. The first commit after checking out an empty repository will
1610 become the initial commit.
1611 * Added the [/help?cmd=all|fossil all dbstat] and
1612 [/help?cmd=all|fossil all info] commands.
1613 * Update SQLite to version 3.8.8.
1614 * Added the --verily option to the [/help?cmd=clean|fossil clean] command.
1615 * Add the "autosync-tries" setting to control the number of autosync attempts
1616 before returning an error.
1617 * Added a compile-time option (--with-miniz) to build using miniz instead
1618 of zlib. Disabled by default.
1619 * Support customization of commands and webpages, including the ability to
@@ -1623,12 +1687,12 @@
1623 [trace], [getParameter], [setParameter], [artifact], and
1624 [globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks.
1625 * Automatically adjust the width of command-line timeline output according to the
1626 detected width of the terminal.
1627 * Prompt the user to optionally fix invalid UTF-8 at check-in.
1628 * Added a line-number toggle option to the [/help?cmd=/info|/info]
1629 and [/help?cmd=/artifact|/artifact] pages.
1630 * Most commands now issue errors rather than silently ignoring unrecognized
1631 command-line options.
1632 * Use full 40-character SHA1 hashes (instead of abbreviations) in most
1633 internal URLs.
1634 * The "ssh:" sync method on Windows now uses "plink.exe" instead of "ssh" as
@@ -1642,11 +1706,11 @@
1642 * Fix a rare and long-standing sync protocol bug
1643 that would silently prevent the sync from running to completion. Before
1644 this bug-fix it was sometimes necessary to do "fossil sync --verily" to
1645 get two repositories in sync.
1646 * Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful
1647 for ad hoc queries in the [/help?cmd=sqlite3|fossil sql] interface,
1648 and also used internally.
1649 * Added the "$secureurl" TH1 variable for use in headers and footers.
1650 * (Internal:) Add the ability to include resources as separate files in the
1651 source tree that are converted into constant byte arrays in the compiled
1652 binary. Use this feature to store the Tk script that implements the --tk
@@ -1666,143 +1730,143 @@
1666 * The [/reports] page now requires Read ("o") permissions. The "byweek"
1667 report now properly propagates the selected year through the event type
1668 filter links.
1669 * The [/help/info | info command] now shows leaf status of the checkout.
1670 * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
1671 * Add option --empty to the "[/help?cmd=open | fossil open]" command.
1672 * Enhanced [/help?cmd=/fileage|the fileage page] to support a glob parameter.
1673 * Add -w|--ignore-all-space and -Z|--ignore-trailing-space options to
1674 [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame],
1675 [/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff].
1676 * Add --strip-trailing-cr option to [/help?cmd=diff|fossil (g)diff] and
1677 [/help?cmd=stash|fossil stash diff].
1678 * Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
1679 and /vdiff UI pages.
1680 * Enhance [/reports?view=byweekday|/reports] with a "byweekday" view.
1681 * Enhance the [/help?cmd=cat|fossil cat] command so that it works outside
1682 of a checkout when using the -R command-line option.
1683 * Use full-length SHA1 hashes, not abbreviations, in most hyperlinks.
1684 * Correctly render the &lt;title&gt; markup on wiki pages in the
1685 [/help?cmd=/artifact|/artifact] webpage.
1686 * Enhance the [/help?cmd=whatis|fossil whatis] command to report on attachments
1687 and cluster artifacts. Added the [/help?cmd=test-whatis-all] command for
1688 testing purposes.
1689 * Add support for HTTP Basic Authentication on [/help?cmd=clone|clone] and
1690 [/help?cmd=sync|sync].
1691 * Fix the [/help?cmd=stash|stash] so that it remembers added files and re-adds
1692 them when the stash is applied.
1693 * Fix the server so that it avoids writing to the database (and thus avoids
1694 database locking issues) on a
1695 [/help?cmd=pull|pull] or [/help?cmd=clone|clone].
1696 * Add support for [./server.wiki#loadmgmt|server load management] using both
1697 a cache of expensive pages (the [/help?cmd=cache|fossil cache] command)
1698 and by rejecting expensive page requests when the server load average is too
1699 high.
1700 * Add the [/help?cmd=praise|fossil praise] command as an alias for
1701 [/help?cmd=blame|fossil blame] for subversion compatibility.
1702 * Enhance the [/help?cmd=test-diff|fossil test-diff] command with -y or --tk
1703 options so that it shows both filenames above their respective columns in
1704 the side-by-side diff output.
1705 * Issue a warning if a [/help?cmd=add|fossil add] command tries to add a file
1706 that matches the ignore-glob.
1707 * Add option -W|--width to "[/help?cmd=stash|fossil stash ls]"
1708 and "[/help?cmd=leaves|fossil leaves]" commands.
1709 * Enhance support for running as the root user. Now works on Haiku.
1710 * Added the <tt>-empty</tt> option to [/help?cmd=new|fossil new], which
1711 causes it to not create an initial empty commit. The first commit after
1712 checking out a repo created this way will become the initial commit.
1713 * Enhance sync operations by committing each round-trip to minimize number
1714 of retransmits when autosync fails. Include option for
1715 [/help?cmd=update| fossil update] and [/help?cmd=merge| fossil merge] to
1716 continue even if missing content.
1717 * Minor portability fixes for platforms where the char type is unsigned
1718 by default.
1719
1720 <h2>Changes For Version 1.28 (2014-01-27)</h2>
1721 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
1722 * When cloning a repository, the user name passed via the URL (if any)
1723 is now used as the default local admin user's name.
1724 * Enhance the SSH transport mechanism so that it runs a single instance of
1725 the "fossil" executable on the remote side, obviating the need for a shell
1726 on the remote side. Some users may need to add the "?fossil=/path/to/fossil"
1727 query parameter to "ssh:" URIs if their fossil binary is not in a standard
1728 place.
1729 * Add the "[/help?cmd=blame | fossil blame]" command that works just like
1730 "fossil annotate" but uses a different output format that includes the
1731 user who made each change and omits line numbers.
1732 * Add the "Tarball and ZIP-archive Prefix" configuration parameter under
1733 Admin/Configuration.
1734 * Fix CGI processing so that it works on web servers that do not
1735 supply REQUEST_URI.
1736 * Add options --dirsonly, --emptydirs, and --allckouts to the
1737 "[/help?cmd=clean | fossil clean]" command.
1738 * Ten-fold performance improvement in large "fossil blame" or
1739 "fossil annotate" commands.
1740 * Add option -W|--width and --offset to "[/help?cmd=timeline | fossil timeline]"
1741 and "[/help?cmd=finfo | fossil finfo]" commands.
1742 * Option -n|--limit of "[/help?cmd=timeline | fossil timeline]" now
1743 specifies the number of entries, just like all other commands which
1744 have the -n|--limit option. The various timeline-related functions
1745 now output "--- ?? limit (??) reached ---" at the end whenever
1746 appropriate. Use "-n 0" if no limit is desired.
1747 * Fix handling of password embedded in Fossil URL.
1748 * New <tt>--once</tt> option to [/help?cmd=clone | fossil clone] command
1749 which does not store the URL or password when cloning.
1750 * Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open
1751 repository.
1752 * Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
1753 * Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
1754 * Advanced possibilities for commit and ticket change notifications over
1755 http using TH1 scripting.
1756 * Add --sha1sum and --integrate options
1757 to the "[/help?cmd=commit | fossil commit]" command.
1758 * Add the "clean" and "extra" subcommands to the
1759 "[/help?cmd=all | fossil all]" command
1760 * Add the --whatif option to "[/help?cmd=clean|fossil clean]" that works the
1761 same as "--dry-run",
1762 so that the name does not collide with the --dry-run option of "fossil all".
1763 * Provide a configuration option to show dates on the web timeline
1764 as "YYMMMDD HH:MM"
1765 * Add an option to the "stats" webpage that allows an administrator to see
1766 the current repository schema.
1767 * Enhancements to the "[/help?cmd=/vdiff|/vdiff]" webpage for more difference
1768 display options.
1769 * Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative
1770 to "/dir" and make it the default way of showing file lists.
1771 * Send gzipped HTTP responses to clients that support it.
1772
1773 <h2>Changes For Version 1.27 (2013-09-11)</h2>
1774 * Enhance the [/help?cmd=changes | fossil changes],
1775 [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
1776 [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
1777 to restrict operation to files and directories named on the command-line.
1778 * New --integrate option to [/help?cmd=merge | fossil merge], which
1779 automatically closes the merged branch when committing.
1780 * Renamed <tt>/stats_report</tt> page to [/reports]. Graph width is now
1781 relative, not absolute.
1782 * Added <tt>yw=YYYY-WW</tt> (year-week) filter to timeline to limit the results
1783 to a specific year and calendar week number, e.g. [/timeline?yw=2013-01].
1784 * Updates to SQLite to prevent opening a repository file using file descriptors
1785 1 or 2 on Unix. This fixes a bug under which an assertion failure could
1786 overwrite part of a repository database file, corrupting it.
1787 * Added support for unlimited line lengths in side-by-side diffs.
1788 * New --close option to [/help?cmd=commit | fossil commit], which
1789 immediately closes the branch being committed.
1790 * Added <tt>chart</tt> option to [/help?cmd=bisect | fossil bisect].
1791 * Improvements to the "human or bot?" determination.
1792 * Reports errors about missing CGI-standard environment variables for HTTP
1793 servers which do not support them.
1794 * Minor improvements to sync support on Windows.
1795 * Added <tt>--scgi</tt> option to [/help?cmd=server | fossil server].
1796 * Internal improvements to the sync process.
1797 * The internals of the JSON API are now MIT-licensed, so downstream
1798 users/packagers are no longer affected by the "do no evil" license
1799 clause.
1800
1801 <h2>Changes For Version 1.26 (2013-06-18)</h2>
1802 * The argument to the --port option for the [/help?cmd=ui | fossil ui] and
1803 [/help?cmd=server | fossil server] commands can take an IP address in addition
1804 to the port number, causing Fossil to bind to just that one IP address.
1805 * After prompting for a password, also ask if that password should be
1806 remembered.
1807 * Performance improvements to the diff engine.
1808 * Fix the side-by-side diff engine to work better with multi-byte Unicode text.
@@ -1809,11 +1873,11 @@
1809 * Color-coding in the web-based annotation (blame) display. Fix the annotation
1810 engine so that it is no longer confused by time-warps.
1811 * The markdown formatter is now available by default and can be used for
1812 tickets, wiki, and embedded documentation.
1813 * Add subcommands "fossil bisect log" and "fossil bisect status" to the
1814 [/help?cmd=bisect | fossil bisect] command, as well as other bisect enhancements.
1815 * Enhanced defenses that prevent spiders from using excessive CPU and bandwidth.
1816 * Consistent use of the -n or --dry-run command line options.
1817 * Win32: Fossil now understands Cygwin paths containing one or more of
1818 the characters <nowiki>"*:<>?|</nowiki>. Those are normally forbidden in
1819 win32. This means that the win32 fossil.exe is better usable in a Cygwin
1820
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,44 +1,108 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_28'>Changes for version 2.28 (pending)</h2><ol>
4 <li> Improvements to [./antibot.wiki|anti-robot defenses]:<ol type="a">
5 <li> The default configuration now allows robots to download any tarball
6 or similar, to better support of automated build systems.
7 <li> New special tag "zipX" for the [/help/robot-restrict|robot-restrict]
8 setting blocks robot access to tarballs, but with exceptions to support
9 automated build systems.
10 <li> Enhancements to the default value for the
11 [/help/robot-restrict|robot-restrict setting].
12 </ol>
13 <li> A drop-down menu of recent branches is now possible for the submenu, and
14 is used in the [/dir?ci=trunk|code browser].
15 <li> Easier access to generated tarballs and ZIPs:<ol type="a">
16 <li> When in the [/dir?ci=trunk|code browser] at the top-level,
17 a new "Download" submenu option is available to take the
18 user to a page where he can download a tarball or ZIP archive.
19 <li> New [/help/www/download|/download page] is available. When
20 configured using the new
21 [/help/suggested-downloads|suggested-downloads setting], a
22 link to [/download] named "Tarballs and ZIPs" appears in the
23 [/sitemap] and thus on the hamburger menu.
24 <li> The filenames for tarballs and ZIPs are now standardized to
25 include a timestamp and a hash prefix.
26 </ol>
27 <li> Timeline enhancements:<ol type="a">
28 <li> A new "Simple" view is available. This is compromise between "Verbose"
29 and "Compact" that shows only the check-in hash rather than the full
30 detail section. There is an ellipsis that one can click on to see the
31 full detail text.
32 <li> The artifact hash in the detail section of each timeline entry is now
33 emphasized (controlled by CSS) to make it easier to locate amid all
34 the other text and links.
35 <li> When clicking on the ellipsis in "Compact" or "Simple" views, the ellipsis
36 is replaced by a left arrow ("←") which can be clicked to hide the extra
37 detail again.
38 <li> New setting [/help/timeline-mark-leaves|timeline-mark-leaves] controls
39 whether or not leaf check-ins are marked in the timeline.
40 <li> "No-graph" timelines (using the "ng" query parameter) now show
41 branch colors and bare check-in circles on the left. The check-in
42 circles appear, but no lines connecting them.
43 ([/timeline?ng|example]).
44 </ol>
45 <li> The [/help/timeline|timeline command] is enhanced with the new
46 "<tt>-u|--for-user</tt>" option.
47 <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
48 can be used to fix a checkout after moving its repository file.
49 </ol>
50
51 <h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
52 <li> Close a potential Denial-of-Service attack against any public-facing Fossil
53 server involving exponential behavior in Fossil's regexp implementation.
54 <li> Fix a SQL injection on the [/help/www/file|/file page]. Thanks to
55 additional defenses built into Fossil, as well as good luck, this injection
56 is not exploitable for either data exfiltration or privilege escalation. The
57 only possible result of invoking the injection is a harmless SQL syntax error.
58 <li> Strengthen robot defenses to help prevent public-facing servers from being
59 overwhelmed by the latest generation of AI spiders.
60 <ol type="a">
61 <li> New javascript captcha used to restrict access by user "nobody" to pages
62 listed in the [/help/robot-restrict|robot-restrict setting].
63 <li> The [/help/robot-exception|robot-exception setting] is available to allow
64 access to pages that match a regular expression. Use this, for example, to
65 allow curl scripts and similar to download release tarballs.
66 <li> Require at least an anonymous login to access the /blame page and similar.
67 </ol>
68 <li> [/help/www/timeline|Timeline] enhancements:
69 <ol type="a">
70 <li> The chng= query parameter on the [/help/www/timeline|timeline page]
71 so that it works with other query parameters like p=, d=, from=, and to=.
72 <li> Always include nodes identify by sel1= and sel2= in the /timeline display.
73 <li> Improved title when p= and d= are different.
74 </ol>
75 <li> Enable the --editor option on the [/help/amend|fossil amend] command.
76 <li> When walking the filesystem looking for Fossil repositories, avoid descending
77 into directories named "/proc".
78 <li> Reduce memory requirements for sending authenticated sync protocol
79 messages.
80 <li> Show numstat-style change statistics in the /info and /ckout pages.
81 <li> Add the [/help/stash | stash rename] subcommand.
82 <li> Add the "-h" option to the "[/help/ls|ls]" command to display
83 file hashes for a specific check-in when in verbose mode.
84 </ol>
85
86 <h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
87 <li>Enhancements to [/help/diff|fossil diff] and similar:
88 <ol type="a">
89 <li> The argument to the --from option can be a directory name, causing
90 Fossil to use files under that directory as the baseline for the diff.
91 <li> For "gdiff", if no [/help/gdiff-command|gdiff-command setting]
92 is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
93 are available, or a --by diff if not.
94 <li> The "Reload" button is added to --tk diffs, to bring the displayed
95 diff up to date with the latest changes on disk.
96 <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
97 diffs of multiple files.
98 </ol>
99 <li>Added the [/help/www/ckout|/ckout web page] to provide information
100 about pending changes in a working check-out
101 <li>Enhancements to the [/help/ui|fossil ui] command:
102 <ol type="a">
103 <li> Defaults to using the new [/help/www/ckout|/ckout page] as its
104 start page. Or, if the new "--from PATH" option is present, the
105 default start page becomes "/ckout?exbase=PATH".
106 <li> The new "--extpage FILENAME" option opens the named file as if it
107 where in a [./serverext.wiki|CGI extension]. Example usage: the
108 person editing this change log has
@@ -46,25 +110,25 @@
110 press "Reload" on the web browser to view edits.
111 <li> Accept both IPv4 and IPv6 connections on all platforms, including
112 Windows and OpenBSD. This also applies to the "fossil server"
113 command.
114 </ol>
115 <li>Enhancements to [/help/merge|fossil merge]:
116 <ol type="a">
117 <li> Added the [/help/merge-info|fossil merge-info] command and
118 especially the --tk option to that command, to provide analysis
119 of the most recent merge or update operation.
120 <li> When a merge conflict occurs, a new section is added to the conflict
121 text that shows Fossil's suggested resolution to the conflict.
122 </ol>
123 <li>Enhancements to [/help/commit|fossil commit]:
124 <ol type="a">
125 <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
126 in the check-in comment, it will alert the developer and give
127 him or her the opportunity to edit the comment before continuing.
128 This feature is controllable by the
129 [/help/verify-comments|verify-comments setting].
130 <li> The new "--if-changes" option causes the commit to become
131 a quiet no-op if there are no pending changes.
132 <li> Added the ability to sign check-ins with SSH keys. Artifacts signed
133 this way are ignored by all previous fossil versions, as if they
134 were plain-text file content instead of Fossil artifacts.
@@ -76,13 +140,13 @@
140 </ol>
141 <li>Deprecate the --comfmtflags and --comment-format global options and
142 no longer list them in the built-in help, but keep them working for
143 backwards compatibility.
144 Alternative TTY comment formatting can still be specified using the
145 [/help/comment-format|comment-format setting], if desired. The
146 default comment format is now called "canonical", not "legacy".
147 <li>Enhancements to the [/help/www/timeline|/timeline page]:
148 <ol type="a">
149 <li> Added the "ml=" ("Merge-in List") query parameter that works
150 like "rl=" ("Related List") but adds "mionly" style related
151 check-ins instead of the full "rel" style.
152 <li> For "tl=", "rl=", and "ml=", the order of the branches in the
@@ -111,33 +175,33 @@
175 in which case the timeline shows those check-ins that are both
176 ancestors of p= and descendants of d=.
177 <li> The saturation and intensity of user-specified checkin and branch
178 background colors are automatically adjusted to keep the colors
179 compatible with the current skin, unless the
180 [/help/raw-bgcolor|raw-bgcolor setting] is turned on.
181 </ol>
182 <li>The [/help/www/docfile|/docfile webpage] was added. It works like
183 /doc but keeps the title of markdown documents with the document rather
184 that moving it up to the page title.
185 <li>Added the [/help/www/clusterlist|/clusterlist page] for analysis
186 and debugging
187 <li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
188 decoding of the artifact described by NAME.
189 <li>Improvements to the [/help/patch|fossil patch] command:
190 <ol type="a">
191 <li> Fix a bug in "fossil patch create" that causes
192 [/help/revert|fossil revert] operations that happened
193 on individualfiles after a [/help/merge|fossil merge]
194 to be omitted from the patch.
195 <li> Added the [/help/patch|patch alias] command for managing
196 aliases for remote checkout names.
197 </ol>
198 <li>Enhancements to on-line help and the [/help/help|fossil help] command:
199 <ol type="a">
200 <li> Add the ability to search the help text, either in the UI
201 (on the [/help/www/search|/search page]) or from the command-line
202 (using the "[/help/search|fossil search -h PATTERN]" command.)
203 <li> Accepts an optional SUBCOMMAND argument following the
204 COMMAND argument and only shows results for the specified
205 subcommand, not the entire command.
206 <li> The -u (--usage) option shows only the command-line syntax
207 <li> The -o (--options) option shows only the command-line options
@@ -153,11 +217,11 @@
217 <li> Link the version field in ticket view to a matching checkin or tag.
218 <li> Show creation time in report and ticket view.
219 <li> Show previous comments in edit ticket as reference.
220 </ol>
221 <li>Added the "hash" query parameter to the
222 [/help/www/whatis|/whatis webpage].
223 <li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
224 which alerts subscribers when an admin creates a new user or
225 when a user's permissions change.
226 <li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
227 the substring "description", then the project description for each repository
@@ -169,44 +233,44 @@
233 <ol type="a">
234 <li> TH1 now makes a distinction between
235 [/doc/trunk/www/th1.md#taint|tainted and untainted string values].
236 This makes it more difficult to write custom TH1 scripts that
237 contain XSS or SQL-injection bugs. The
238 [/help/vuln-report|vuln-report] setting was added to control
239 what Fossil does when it encounters a potential TH1
240 security problem.
241 <li> The "--th" option was removed from the [/help/pikchr|fossil pikchr]
242 command.
243 <li> The "enable_htmlify" TH1 command was removed.
244 </ol>
245 <li>Make [/help/www/chat|/chat] better-behaved during server outages, reducing
246 the frequency of reconnection attempts over time and providing feedback
247 to the user when the connection is down.
248 <li>The [/help/www/sqlar|/sqlar] page does not work for users who are not logged
249 in, nor are links to that page displayed to users who are not logged in. Being
250 logged in as "anonymous" is sufficient to overcome this restriction, assuming
251 that "anonymous" can download tarballs and ZIP archives.
252 <li>Many other minor fixes and additions.
253 </ol>
254
255 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
256
257 * The "[/help/ui|fossil ui /]" command now works even for repositories
258 that have non-ASCII filenames
259 * Add the [/help/tree|fossil tree] command.
260 * On case-insensitive filesystems, store files using the filesystem's
261 preferred case rather than the case typed in by the user.
262 * Change the name "fossil cherry-pick" command to "fossil cherrypick",
263 which is more familiar to Git users. Retain the legacy name for
264 compatibility.
265 * Add new query parameters to the [/help/www/timeline|/timeline page]:
266 d2=, p2=, and dp2=.
267 * Add options to the [/help/tag|fossil tag] command that will list tag values.
268 * Add the -b|--brief option to the [/help/status|fossil status] command.
269 * Add ability to upload unversioned files via the [/help/www/uvlist|/uvlist page].
270 * Add history search to the [/help/www/chat|/chat page].
271 * Add Unix socket support to the [/help/server|server command].
272 * On Windows, use the root certificates managed by the operating system
273 (requires OpenSSL 3.2.0 or greater).
274 * Take into account zero-width and double-width unicode characters when
275 formatting the command-line timeline.
276 * Update the built-in SQLite to version 3.47.0. Precompiled binaries are
@@ -243,11 +307,11 @@
307 </ul>
308 * If an "ssh:" sync fails in a way that suggests that the fossil executable
309 could not be found on the remote host, then retry after adding a PATH=
310 prefix to the command. This helps "ssh:" to "just work" when the server
311 is a Mac.
312 * Enhancements to the [/help/www/timeline|/timeline page]:
313 <ul>
314 <li> Add the x= query paramater
315 <li> Add the shortcut tl= and rl= query parameters
316 <li> Add support for from=,ft= and from=,bt= query parameter combinations
317 <li> Automatically highlight the endpoints for from=,to= queries.
@@ -257,16 +321,16 @@
321 * Moved the /museum/repo.fossil file referenced from the Dockerfile from
322 the ENTRYPOINT to the CMD part to allow use of --repolist mode.
323 * The [/uvlist] page now shows the hash algorithm used so that
324 viewers don't have to guess. The hash is shown in a fixed-width
325 font for a more visually pleasing display.
326 * If the [/help/autosync|autosync setting] contains keyword "all",
327 the automatic sync occurs against all defined remote repositories, not
328 just the default.
329 * Markdown formatter: improved handling of indented fenced code blocks
330 that contain blank lines.
331 * When doing a "[/help/add|fossil add]" on a system with case-insensitive
332 but case-preserving filenames (Mac and Windows) try to use the filename
333 case as it is known to the filesystem, not the case entered by the
334 user on the command-line. See
335 [forum:/forumpost/30d9c0d131610f53|forum thread 30d9c0d131610f53].
336 * Fix problems with one-click unsubscribe on email notifications.
@@ -280,17 +344,17 @@
344 <h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
345
346 * Add ability to "close" forum threads, such that unprivileged users
347 may no longer respond to them. Only administrators can close
348 threads or respond to them by default, and the
349 [/help/forum-close-policy|forum-close-policy setting] can be
350 used to add that capability to moderators.
351 * Add the [/help/all|fossil all whatis] command.
352 * The [/help/status|fossil status] command and relevant UI pages now
353 correctly report files which were both renamed <b>and</b> edited as such.
354 * Show default value of settings that have a default in
355 [/help/help|fossil help SETTING] output.
356 * On timeline graphs, show closed check-ins using an X in the middle of the
357 node circle or box.
358 * New options for email notification: Get email only for the first
359 post in each new thread, and/or posts that are in reply to my posts.
360 * Fix a regression bug introduced in version 2.22 that caused FTS5 searches
@@ -303,35 +367,35 @@
367 <li> Better defense against cross-site request forgery (CSRF)
368 attacks.
369 <li> Improvements to static analysis of source code (the codecheck1.c
370 file in the source tree).
371 </ul>
372 * Enhance the [/help/www/dir|treeview file listings]
373 ([/dir?type=tree&ci=trunk|example]) by displaying file sizes
374 and adding the option to sort by file size.
375 * The [/help/fts-config|fossil fts-config] command now shows how much
376 repository space is used by the full-text index.
377 * Changing a setting to an empty string is now the same as deleting the
378 setting, in most cases. There are a few exceptions, indicated by the
379 keep-empty flag on the setting definition.
380 * The [/help/branch|fossil branch list] command can now filter branches
381 that have/have not been merged into the current branch.
382 * Improvements to interactions with remote repositories over SSH:
383 <ul>
384 <li> Print the text of the SSH command that is run to do remote interaction,
385 for full disclosure to the operator.
386 <li> Add a PATH= argument to the [/help/ui|fossil ui remote:/] and
387 [/help/patch|fossil patch push/pull remote:...] commands so that
388 they work when the "remote" machine is a Mac and the "fossil"
389 executable is in the $HOME/bin directory.
390 </ul>
391 * Update built-in libraries SQLite, ZLib, Pikchr to their latest versions.
392 * Documentation enhancements and typo fixes.
393
394
395 <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2>
396 * Enhancements to the [/help/www/timeline|/timeline webpage]: <ol type="a">
397 <li> Add the ft=TAG query parameter which in combination with d=Y
398 shows all descendants of Y up to TAG
399 <li> Enhance the s=PATTERN (search) query parameter so that forum post
400 text is also searched when the "vfx" query parameter is used
401 <li> Fix the u= (user) query parameter so that it works with a= and b=
@@ -355,56 +419,56 @@
419 searching in Chinese.
420 * Comment lines (starting with a '#') are now supported inside
421 [./settings.wiki#versionable|versioned settings].
422 * Default permissions for anonymous users in new repositories are
423 changed to "hz".
424 * The [/help/status|fossil status] command now detects when a
425 file used to be a symlink and has been replaced by a regular file.
426 (It previously checked for the inverse case only.)
427 * The [/help/empty-dirs|empty-dirs setting] now reuses the same
428 parser as the *-glob settings instead of its prior idiosyncratic
429 parser, allowing quoted whitespace in patterns.
430 * Enhancements to the [/help/www/reports|/reports webpage]:
431 <ol type="a">
432 <li> The by-week, by-month, and by-year options now show an estimated
433 size of the current week, month, or year as a dashed box.
434 <li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins".
435 </ol>
436
437 <h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2>
438 * Users can request a password reset. This feature is disabled by default.
439 Use the new [/help/self-pw-reset|self-pw-reset property] to enable it.
440 New web pages [/help/www/resetpw|/resetpw] and
441 [/help/www/reqpwreset|/reqpwreset] added.
442 * Add the [/help/repack|fossil repack] command (together with
443 [/help/all|fossil all repack]) as a convenient way to optimize the
444 size of one or all of the repositories on a system.
445 * Add the ability to put text descriptions on ticket report formats.
446 * Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
447 * The [/help/www/chat|/chat page] can now embed fossil-rendered
448 views of wiki/markdown/pikchr file attachments with the caveat that such
449 embedding happens in an iframe and thus does not inherit styles and such
450 from the containing browser window.
451 * The [/help/all|fossil all remote] subcommand added to "fossil all".
452 * Passwords for remembered remote repositories are now stored as irreversible
453 hashes rather than obscured clear-text, for improved security.
454 * Add the "nossl" and "nocompress" options to CGI.
455 * Update search infrastructure from FTS4 to FTS5.
456 * Add the [/help/www/deltachain|/deltachain] page for debugging purposes.
457 * Writes to the database are disabled by default if the HTTP request
458 does not come from the same origin. This enhancement is a defense in depth
459 measure only; it does not address any known vulnerabilities.
460 * Improvements to automatic detection and mitigation of attacks from
461 malicious robots.
462
463 <h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
464 * Added the [/help/chat-timeline-user|chat-timeline-user setting]. If
465 it is not an empty string, then any changes that would appear on the timeline
466 are announced in [./chat.md|the chat room].
467 * The /unsubscribe page now requests confirmation. [./alerts.md|Email notifications]
468 now contain only an "Unsubscribe" link, and not a link to subscription management.
469 * Added the "[/help/branch|fossil branch lsh]" subcommand to list the
470 most recently modified branches.
471 * More elements of the /info page are now inside of an accordion.
472 * Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all
473 commands which still used the former name, for consistency.
474 * Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch"
@@ -434,11 +498,11 @@
498 accomplished using a Common Table Expression in the underlying SQL.
499 * Sort tag listings (command line and webpage) by taking numbers into
500 consideration so as to cater for tags that follow semantic versioning.
501 * On the wiki listings, omit by default wiki pages that are associated with
502 check-ins and branches.
503 * Add the new "[/help/describe|fossil describe]" command.
504 * Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support].
505 See corresponding [../test/markdown-test3.md|test cases],
506 [/wiki?name=branch/markdown-footnotes#il|known limitations] and
507 [forum:/forumthread/ee1f1597e46ec07a|discussion].
508 * Add the new special name "start:BRANCH" to refer to the first check-in of
@@ -450,96 +514,96 @@
514 operation. Also require explicit "system" proxy setting to use
515 "http_proxy" environment variable.
516 * Reimplemented the [/pikchrshow] app to use a WebAssembly build of
517 pikchr so that it can render pikchrs on the client instead of requiring
518 a server round-trip.
519 * Add the [/help/email-listid|email-listid setting]. If set, it is
520 used as the List-ID header for all outbound notification emails.
521 * Add the "--branch" option to the "[/help/timeline|timeline]" command
522 to restrict the displayed items to a specific branch.
523 * Add the "--versions" option to "[/help/diff|fossil diff]"
524 to display details about the compared versions into the patch header.
525 * Numerous other minor enhancements.
526
527 <h2 id='v2_18'>Changes for version 2.18 (2022-02-23)</h2>
528 * Added support for [./ssl-server.md|SSL/TLS server mode] for commands
529 like "[/help/server|fossil server]" and "[/help/http|fossil http]"
530 * The new [/help/cherry-pick|cherry-pick command] is an alias for
531 [/help/merge|merge --cherrypick].
532 * Add new setting "[/help/large-file-size|large-file-size]". If the size
533 of any file in a commit exceeds this size, a warning is issued.
534 * Query parameter "year=YYYY" is now accepted by [/help/www/timeline|/timeline].
535 * The [/help/tar|tar] and [/help/zip|zip commands] no longer
536 sterilize the manifest file.
537 * Further improvement to diff alignment in cases that involve both
538 edits and indentation changes.
539 * [/doc/trunk/www/chat.md|Chat] improvements:<ul>
540 <li> [/help/www/chat|The /chat page] input options have been reworked
541 again for better cross-browser portability.
542 <li> When sending a [/help/www/chat|/chat] message fails, it is no longer
543 immediately lost and sending may optionally be retried.
544 <li> [/help/www/chat|/chat] can now optionally embed attachments of certain
545 types directly into message bodies via an iframe.
546 <li> Add the "--as FILENAME" option to the "[/help/chat|fossil chat send]"
547 command.
548 <li> Added the "[/help/chat|fossil chat pull]" command, available to
549 administrators only, for backing up the chat conversation.
550 </ul>
551 * Promote the test-detach command into the [/help/detach|detach command].
552 * For "[/help/pull|fossil pull]" with the --from-parent-project option,
553 if no URL is specified then use the last URL from the most recent prior
554 "fossil pull --from-parent-project".
555 * Add options --project-name and --project-desc to the
556 "[/help/init|fossil init]" command.
557 * The [/help/www/ext|/ext page] generates the SERVER_SOFTWARE environment
558 variable for clients.
559 * Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such
560 that it includes the query string. This is how most other systems understand
561 REQUEST_URI.
562 * Added the --transport-command option to [/help/sync|fossil sync]
563 and similar.
564
565 <h2 id='v2_17'>Changes for version 2.17 (2021-10-09)</h2>
566
567 * Major improvements to the "diff" subsystem, including: <ul>
568 <li> Added new [/help/diff|formatting options]: --by, -b, --webpage, --json, --tcl.
569 <li> Partial-line matching for unified diffs
570 <li> Better partial-line matching for side-by-side diffs
571 <li> Buttons on web-based diffs to show more context
572 <li> Performance improvements
573 </ul>
574 * The --branchcolor option on [/help/commit|fossil commit] and
575 [/help/amend|fossil amend] can now take the value "auto" to
576 force Fossil to use its built-in automatic color choosing algorithm.
577 * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running
578 [/help/open|fossil open].
579 * Add the [/help/ticket-default-report|ticket-default-report setting],
580 which if set to the title of a ticket report causes that ticket report
581 to be displayed below the search box in the /ticket page.
582 * The "nc" query parameter to the [/help/www/timeline|/timeline] page
583 causes all graph coloring to be omitted.
584 * Improvements and bug fixes to the new "[/help/ui|fossil ui REMOTE]"
585 feature so that it works better on a wider variety of platforms.
586 * In [/help/www/wikiedit|/wikiedit], show the list of attachments for
587 the current page and list URLs suitable for pasting them into the page.
588 * Add the --no-http-compression option to [/help/sync|fossil sync]
589 and similar.
590 * Print total payload bytes on a [/help/sync|fossil sync] when using
591 the --verbose option.
592 * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
593 </tt>unhide</tt> subcommands to [/help/branch|the branch command].
594 * The "-p" option to [/help/branch|fossil branch list] shows only
595 private branches.
596 * The [/md_rules|Markdown formatter] now interprets the content of
597 block HTML markup (such as &lt;table&gt;) in most cases. Only content
598 of &lt;pre&gt; and &lt;script&gt; is passed through verbatim.
599 * The [/help/wiki|wiki list command] no longer lists "deleted"
600 pages by default. Use the new <tt>--all</tt> option to include deleted
601 pages in the output.
602 * The [/help/all|fossil all git status] command only shows reports for
603 the subset of repositories that have a configured Git export.
604 * The [/help/www/chat|/chat] configuration was reimplemented and
605 provides new options, including the ability for a repository
606 administrator to
607 [./chat.md#notifications|extend the selection of notification sounds]
608 using unversioned files.
609 * Chat now uses fossil's full complement of markdown features,
@@ -548,14 +612,14 @@
612 markdown-processed when they are sent instead of when they
613 are saved.
614 * Added a chat message preview mode so messages can be previewed
615 before being sent. Similarly, added a per-message ability to view
616 the raw un-parsed message text.
617 * The hotkey to activate preview mode in [/help/www/wikiedit|/wikiedit],
618 [/help/www/fileedit|/fileedit], and [/help/www/pikchrshow|/pikchrshow]
619 was changed from ctrl-enter to shift-enter in order to align with
620 [/help/www/chat|/chat]'s new preview feature and related future
621 changes.
622
623 <h2 id='v2_16'>Changes for Version 2.16 (2021-07-02)</h2>
624 * <b>Security:</b> Fix the client-side TLS so that it verifies that the
625 server hostname matches its certificate.
@@ -562,11 +626,11 @@
626 * The default "ssh" command on Windows is changed to "ssh" instead of the
627 legacy "plink", as ssh is now generally available on Windows systems.
628 Installations that still need to use the legacy "plink" can make that
629 happen by running: '<tt>fossil set ssh-command "plink -ssh" --global</tt>'.
630 * Added the [./patchcmd.md|fossil patch] command.
631 * The [/help/ui|fossil ui] command is enhanced in multiple ways:<ol>
632 <li> The REPOSITORY argument can be the name of a check-out directory.
633 <li> If the REPOSITORY argument is prefixed by "HOST:" or "USER@HOST:"
634 then the ui is run on the remote machine and tunnelled back to the local
635 machine using ssh. (The latest version of fossil must be installed on
636 both the local and the remote for this to work correctly.)
@@ -575,25 +639,25 @@
639 * The [/brlist|/brlist web page] allows the user to
640 select multiple branches to be displayed together in a single
641 timeline.
642 * The [./forum.wiki|Forum] provides a hyperlink on the author of each
643 post that goes to a timeline of recent posts by that same author.
644 * Added the "[/help/bisect|fossil bisect run]" command for improved
645 automation of bisects.
646 * The [/help/merge|fossil merge] command now does a better job merging
647 branches where files have been renamed between the current branch and the
648 branch being merged.
649 * The [/help/open|fossil open] command allows the repository file
650 to be inside the working directory without requiring the --force flag.
651 * The [/help/www/wikiedit|/wikiedit] and [/help/www/wikinew|/wikinew]
652 pages now default to markdown format.
653 * The [/help/www/login|/login] page now links to a user's forum post
654 timeline if the repository has forum posts.
655 * Tags may now be propagated for forum posts, wiki pages, and technotes.
656 The [/help/tag|tag command] can now manipulate and list such tags.
657 * [./caps/login-groups.md|Login-Groups] are now shown on the repository
658 list of the "[/help/all|fossil all ui]" command.
659 * Administrators can configure [./alerts.md|email alerts] to expire
660 a specific number of days (ex: 365) after the last user contact with
661 the Fossil server. This prevents alert emails being sent to
662 abandoned email accounts forever.
663 * SQL that defines [/tktsetup_tab|database objects for tickets] now
@@ -610,11 +674,11 @@
674 * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
675 the patch is recommended.</b>
676 * The [./defcsp.md|default CSP] has been relaxed slightly to allow
677 images to be loaded from any URL. All other resources are still
678 locked down by default.
679 * The built-in skins all use the "[/help/mainmenu|mainmenu]"
680 setting to determine the content of the main menu.
681 The ability to edit the
682 "mainmenu" setting is added on the /Admin/Configuration page.
683 * The hamburger menu is now available on most of the built-in skins.
684 * Any built-in skin named "X" can be used instead of the standard
@@ -624,40 +688,40 @@
688 included. The [/skins] page may be used to select a skin.
689 * The [/cookies] page now gives the user an opportunity to delete
690 individual cookies. And the /cookies page is linked from the
691 /sitemap, so that it appears in hamburger menus.
692 * The [/sitemap] extensions are now specified by a single new
693 "[/help/sitemap-extra|sitemap-extra setting]",
694 rather than a cluster of various
695 "sitemap-*" settings. The older settings are no longer used.
696 <b>This change might require minor server configuration
697 adjustments on servers that use /sitemap extensions.</b>
698 The /Admin/Configuration page provides the ability to edit
699 the new "sitemap-extra" setting.
700 * Added the "--ckout-alias NAME" option to
701 [/help/ui|fossil ui], [/help/server|fossil server], and
702 [/help/http|fossil http]. This option causes Fossil to
703 understand URIs of the form "/doc/NAME/..." as if they were
704 "[/help/www/doc|/doc/ckout/...]", to facilitate testing of
705 [./embeddeddoc.wiki|embedded documentation] changes prior to
706 check-in.
707 * For diff web pages, if the diff type (unified versus side-by-side)
708 is not specified by a query parameter, and if the
709 "[/help/preferred-diff-type|preferred-diff-type]"
710 setting is omitted or less than 1, then select the diff type based
711 on a guess of whether or not the request is coming from a mobile
712 device. Mobile gets unified and desktop gets side-by-side.
713 * The various pages which show diffs now have toggles to show/hide
714 individual diffs.
715 * Add the "[/help/preferred-diff-type|preferred-diff-type]"
716 setting to allow an admin to force a default diff type.
717 * The "pikchr-background" setting is now available in
718 "detail.txt" skin files, for better control of Pikchr
719 colors in inverted color schemes.
720 * Add the <tt>--list</tt> option to the
721 [/help/tarball|tarball],
722 [/help/zip|zip], and [/help/sqlar|sqlar]
723 commands.
724 * The javascript used to implement the hamburger menu on the
725 default built-in skin has been made generic so that it is usable
726 by a variety of skins, and promoted to an ordinary built-in
727 javascript file.
@@ -665,24 +729,24 @@
729 "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
730 "[/doc/trunk/www/th1.md#capexpr|capexpr]",
731 "foreach", "lappend", and "string match"
732 * The [/help/leaves|leaves command] now shows the branch point
733 of each leaf.
734 * The [/help/add|fossil add] command refuses to add files whose
735 names are reserved by Windows (ex: "aux") unless the --allow-reserved
736 option is included. This helps prevent Unix users from accidentally
737 creating check-ins that are unreadable by Windows users.
738 * Add the "re=" query parameter to the [/help/www/dir|/dir] webpage,
739 for symmetry with the [/help/www/tree|/tree] page.
740 * Update the built-in SQLite to version 3.35.0.
741 * The ./configure script now has the --print-minimum-sqlite-version option
742 that prints the minimum SQLite version required by the current version
743 of Fossil. This might be used by integrators who insist on building
744 Fossil to link against the system SQLite library rather than the
745 built-in copy of SQLite, to verify that their system SQLite library
746 is recent enough.
747 * Webpage that shows [/help/www/whistory|history of a wiki page]
748 gained client-side UI to help with comparison between two arbitrary
749 versions of a wiki (by the means of anchoring a "baseline" version)
750 and the ability to squeeze several sequential edits made by the same
751 user into a single "recycled" row (the latest edit in that sequence).
752
@@ -702,40 +766,40 @@
766 version 2.14 and then later downgrade or otherwise use an earlier
767 version of Fossil, the email notification mechanism may fail
768 to send out notifications for some events, due to the missing
769 trigger. If you want to
770 permanently downgrade an installation, then you should run
771 "[/help/rebuild|fossil rebuild]" after the downgrade
772 to get email notifications working again. If you are not using
773 email notification, then the schema change will not affect you in
774 any way.
775 * <b>Schema Update Notice #2:</b>
776 This release changes how the descriptions of wiki edits are stored
777 in the EVENT table, for improved display on timelines. You must
778 run "[/help/rebuild|fossil rebuild]" to take advantage of
779 this enhancement. Everything will still work without
780 "fossil rebuild", except you will get goofy descriptions of
781 wiki updates in the timeline.
782 * Add support for [./chat.md|Fossil chat].
783 * The "[/help/clone|fossil clone]" command is enhanced so that
784 if the repository filename is omitted, an appropriate name is derived
785 from the remote URL and the newly cloned repo is opened. This makes
786 the clone command work more like Git, thus making it easier for
787 people transitioning from Git.
788 * Added the --mainbranch option to the [/help/git|fossil git export]
789 command.
790 * Added the --format option to the
791 "[/help/timeline|fossil timeline]" command.
792 * Enhance the --numstat option on the
793 "[/help/diff|fossil diff]" command so that it shows a total
794 number of lines added and deleted and total number of files
795 modified.
796 * Add the "contact" sub-command to [/help/user|fossil user].
797 * Added commands "[/help/all|fossil all git export]" and
798 "[/help/all|fossil all git status]".
799 * Added the "df=CHECKIN" query parameter to the
800 [/help/www/timeline|/timeline page].
801 * Improvements to the "[/sitemap]" page. Add subpages
802 [/sitemap-timeline] and [/sitemap-test].
803 * Better text position in cylinder objects of Pikchr diagrams.
804 * New "details.txt" settings available to custom skins to better control
805 the rendering of Pikchr diagrams:
@@ -757,24 +821,24 @@
821 * Added support for [./interwiki.md|interwiki links].
822 * Enable &lt;del&gt; and &lt;ins&gt; markup in wiki.
823 * Improvements to the Forum threading display.
824 * Added support for embedding [./pikchr.md|pikchr]
825 markup in markdown and fossil-wiki content.
826 * The new "[/help/pikchr|pikchr]" command can render
827 pikchr scripts, optionally pre-processed with
828 [/doc/trunk/www/th1.md|TH1] blocks and variables exactly like
829 site skins are.
830 * The new [/help/www/pikchrshow|pikchrshow] page provides an
831 editor and previewer for pikchr markup.
832 * In [/help/www/wikiedit|/wikiedit] and
833 [/help/www/fileedit|/fileedit], Ctrl-Enter can now be used
834 initiate a preview and to toggle between the editor and preview
835 tabs.
836 * The <tt>/artifact</tt> and <tt>/file</tt> views, when in
837 line-number mode, now support interactive selection of a range
838 of lines to hyperlink to.
839 * Enhance the [/help/www/finfo|/finfo] webpage so that when query
840 parameters identify both a filename and a checkin, the resulting
841 graph tracks the identified file across renames.
842 * The built-in SQLite is updated to an alpha of version 3.34.0, and
843 the minimum SQLite version is increased to 3.34.0 because the
844 /finfo change in the previous bullet depends on enhancements to
@@ -783,18 +847,18 @@
847 * Countless other minor refinements and documentation improvements.
848
849 <h2 id='v2_12'>Changes for Version 2.12.1 (2020-08-20)</h2>
850
851 * (2.12.1): Fix client-side vulnerabilities discovered by Max Justicz.
852 * Security fix in the "[/help/git|fossil git export]" command.
853 The same fix is also backported to version 2.10.1 and 2.11.1.
854 New "safety-net" features were added to prevent similar problems
855 in the future.
856 * Enhancements to the graph display for cases when there are
857 many cherry-pick merges into a single check-in.
858 [/timeline?f=2d75e87b760c0a9|Example]
859 * Enhance the [/help/open|fossil open] command with the new
860 --workdir option and the ability to accept a URL as the repository
861 name, causing the remote repository to be cloned automatically.
862 Do not allow "fossil open" to open in a non-empty working directory
863 unless the --keep option or the new --force option is used.
864 * Enhance the markdown formatter to more closely follow the
@@ -803,65 +867,65 @@
867 Underscores in the middle of identifiers (ex: fossil_printf())
868 no longer need to be escaped.
869 * The markdown-to-html translator can prevent unsafe HTML
870 (for example: &lt;script&gt;) on user-contributed pages like forum and
871 tickets and wiki. The admin can adjust this behavior using
872 the [/help/safe-html|safe-html setting] on the Admin/Wiki page.
873 The default is to disallow unsafe HTML everywhere.
874 [https://fossil-scm.org/forum/forumpost/3714e6568f|Example].
875 * Added the "collapse" and "expand" capability for long forum posts.
876 [https://fossil-scm.org/forum/forumpost/9297029862|Example]
877 * The "[/help/remote-url|fossil remote]" command now has options for
878 specifying multiple persistent remotes with symbolic names. Currently
879 only one remote can be used at a time, but that might change in the
880 future.
881 * Add the "Remember me?" checkbox on the login page. Use a session
882 cookie for the login if it is not checked.
883 * Added the experimental "[/help/hook|fossil hook]" command for
884 managing "hook scripts" that run before checkin or after a push.
885 * Enhance the [/help/revert|fossil revert] command so that it
886 is able to revert all files beneath a directory.
887 * Add the [/help/bisect|fossil bisect skip] command.
888 * Add the [/help/backup|fossil backup] command.
889 * Enhance [/help/bisect|fossil bisect ui] so that it shows all unchecked
890 check-ins in between the innermost "good" and "bad" check-ins.
891 * Added the <tt>--reset</tt> flag to the "[/help/add|fossil add]",
892 "[/help/rm|fossil rm]", and
893 "[/help/addremove|fossil addremove]" commands.
894 * Added the "<tt>--min</tt> <i>N</i>" and "<tt>--logfile</tt> <i>FILENAME</i>"
895 flags to the [/help/backoffice|backoffice] command, as well as other
896 enhancements to make the backoffice command a viable replacement for
897 automatic backoffice. Other incremental backoffice improvements.
898 * Added the [/help/www/fileedit|/fileedit page], which allows
899 editing of text files online. Requires explicit activation by
900 a setup user.
901 * Translate built-in help text into HTML for display on web pages.
902 [/help/help|Example].
903 * On the [/help/www/timeline|/timeline] webpage, the combination
904 of query parameters "p=CHECKIN" and "bt=ANCESTOR" draws all
905 ancestors of CHECKIN going back to ANCESTOR. For example,
906 [/timeline?p=202006271506&bt=version-2.11] shows all ancestors
907 of the checkin that occurred on 2020-06-27 15:06 going back to
908 the 2.11 release.
909 * Update the built-in SQLite so that the
910 "[/help/sql|fossil sql]" command supports new output
911 modes ".mode box" and ".mode json".
912 * Add the "<tt>obscure()</tt>" SQL function to the
913 "[/help/sql|fossil sql]" command.
914 * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
915 the "[/help/sql|fossil sql]" command, providing access to the
916 dispatch table including all help text, and the builtin data files,
917 respectively.
918 * [./delta_format.wiki|Delta compression] is now applied to forum edits.
919 * The [/help/www/wikiedit|wiki editor] has been modernized and is
920 now Ajax-based. The WYSIWYG editing option for Fossil-format wiki
921 pages was removed. (Please let us know, via the site's Forum menu,
922 if that removal unduly impacts you.) This also changes the semantics
923 of the wiki "Sandbox": that pseudo-page may be freely edited but
924 no longer saved via the UI (the [/help/wiki|wiki CLI command]
925 can, though).
926 * The [/help/allow-symlinks|allow-symlinks setting] no longer
927 syncs. It must be activated individually on any clones which require
928 symlinks.
929 * Countless documentation enhancements.
930
931 <h2 id='v2_11'>Changes for Version 2.11 (2020-05-25)</h2>
@@ -873,46 +937,46 @@
937 can now omit punctation. So, for example, "202004181942" and
938 "2020-04-18 19:42" mean the same thing.
939 * Enhance backlink processing so that it works with Markdown-formatted
940 tickets and so that it works for wiki pages.
941 Ticket [a3572c6a5b47cd5a].
942 <ul><li> "[/help/rebuild|fossil rebuild]" is needed to
943 take full advantage of this fix. Fossil will continue
944 to work without the rebuild, but the new backlinks will be missing.</ul>
945 * The algorithm for finding the
946 [./tech_overview.wiki#configloc|location of the configuration database]
947 is enhanced to be XDG-compliant.
948 * Add a hide/show feature to
949 [./wikitheory.wiki#assocwiki|associated wiki] display on
950 check-in and branch information pages.
951 * Enhance the "[/help/info|fossil info]" command so that it
952 works with no arguments even if not within an open check-out.
953 * Many improvements to the forum and especially email notification
954 of forum posts, in response to community feedback after switching
955 SQLite support from a mailing list over to the forum.
956 * Minimum length of a self-registered user ID increased from 3 to 6
957 characters.
958 * When the "vfx" query parameter is used on the
959 "[/help/www/timeline|/timeline]" page, it causes the complete
960 text of forum posts to be displayed.
961 * Rework the "[/help/grep|fossil grep]" command to be more useful.
962 * Expose the [/help/redirect-to-https|redirect-to-https]
963 setting to the [/help/settings|settings] command.
964 * Improve support for CGI on IIS web servers.
965 * The [./serverext.wiki|/ext page] can now render index files,
966 in the same way as the embedded docs.
967 * Most commands now support the Unix-conventional "<tt>--</tt>"
968 flag to treat all following arguments as filenames
969 instead of flags.
970 * Added the [/help/mimetypes|mimetypes config setting]
971 (versionable) to enable mimetype overrides and custom definitions.
972 * Add an option on the /Admin/Timeline setup page to set a default
973 timeline style other than "Modern".
974 * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
975 of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
976 into the check-in hash for the document currently being viewed.
977 * Added the [/help/www/phantoms|/phantoms] webpage that shows all
978 phantom artifacts.
979 * Enhancements to phantom processing to try to reduce
980 bandwidth-using chatter about phantoms on the sync protocol.
981 * Security: Fossil now assumes that the schema of every
982 database it opens has been tampered with by an adversary and takes
@@ -920,23 +984,23 @@
984 * Security: Fossil now puts the Content-Security-Policy in the
985 HTTP reply header, in addition to also leaving it in the
986 HTML &lt;head&gt; section, so that it is always available, even
987 if a custom skin overrides the HTML &lt;head&gt; and omits
988 the CSP in the process.
989 * Output of the [/help/diff|fossil diff -y] command automatically
990 adjusts according to the terminal width.
991 * The Content-Security-Policy is now set using the
992 [/help/default-csp|default-csp setting].
993 * Merge conflicts caused via the [/help/merge|merge] and
994 [/help/update|update] commands no longer leave temporary
995 files behind unless the new <tt>--keep-merge-files</tt> flag
996 is used.
997 * The [/help/www/artifact_stats|/artifact_stats page] is now accessible
998 to all users if the new "artifact_stats_enable" setting is turned
999 on. There is a new checkbox under the /Admin/Access menu to turn
1000 that capability on and off.
1001 * Add the [/help/tls-config|fossil tls-config] command for viewing
1002 the TLS configuration and the list of SSL Cert exceptions.
1003 * Captchas all include a button to read the captcha using an audio
1004 file, so that they can be completed by the visually impaired.
1005 * Stop using the IP address as part of the login cookie.
1006 * Bug fix: fix the SSL cert validation logic so that if an exception
@@ -957,27 +1021,27 @@
1021 <h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2>
1022
1023 * (2.10.2): backport security fixes from 2.12.1
1024 * (2.10.1): backport security fix for the "fossil git export" command.
1025 * Added support for [./serverext.wiki|CGI-based Server Extensions].
1026 * Added the [/help/repolist-skin|repolist-skin] setting used to
1027 add style to repository list pages.
1028 * Enhance the hierarchical display of Forum threads to do less
1029 indentation and to provide links back to the previous message
1030 in the thread. Provide sequential numbers for all messages in
1031 a forum thread.
1032 * Add support for fenced code blocks and improved hyperlink
1033 processing to the [/md_rules|markdown formatter].
1034 * Add support for hyperlinks to wiki pages in the
1035 [/md_rules|markdown formatter].
1036 * Enhance the [/help/www/stat|/stat] page so that it gives the
1037 option to show a breakdown of forum posts.
1038 * The special check-in name "merge-in:BRANCH" means the source of
1039 the most recent merge-in from the parent branch of BRANCH.
1040 * Add hyperlinks to branch-diffs on the /info page and from
1041 timelines of a branch.
1042 * Add graphical context on the [/help/www/vdiff|/vdiff] page.
1043 * Uppercase query parameters, POST parameters, and cookie names are
1044 converted to all lowercase and entered into the parameter set,
1045 instead of being discarded.
1046 * Change the default [./hashpolicy.wiki|hash policy] to SHA3.
1047 * Timeout [./server/any/cgi.md|CGI requests] after 300 seconds, or
@@ -990,14 +1054,14 @@
1054 * Performance optimizations.
1055 * Many documentation improvements.
1056
1057 <h2 id='v2_9'>Changes for Version 2.9 (2019-07-13)</h2>
1058
1059 * Added the [/help/git|fossil git export] command and instructions
1060 for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project].
1061 * Improved handling of relative hyperlinks on the
1062 [/help/www/artifact|/artifact] pages for wiki. For example,
1063 hyperlinks and the lizard &lt;img&gt; now work correctly
1064 for both [/artifact/2ff24ab0887cf522] and
1065 [/doc/0d7ac90d575004c2415/www/index.wiki].
1066 * Enhancements to the timeline graph layout, to show more information
1067 with less clutter.
@@ -1006,28 +1070,28 @@
1070 configuration.
1071 * Copy buttons added to various check-in hash and branch name links.
1072 * Double-clicking on a /timeline graph node now jumps to the /info page
1073 for the check-in. So, repurpose the timestamp hyperlink to show all
1074 activity around that check-in in time.
1075 * Added the [/help/touch|fossil touch] command, and the --setmtime
1076 option on the [/help/open|fossil open] and
1077 [/help/update|fossil update] commands.
1078 * Many documentation enhancements.
1079 * For the "[/help/update|fossil update]" and
1080 "[/help/checkout|fossil checkout]" commands, if a
1081 managed file is removed because it is no longer part of the target
1082 check-in and the directory containing the file is empty after the
1083 file is removed and the directory is not the current working
1084 directory and is not on the [/help/empty-dirs|empty-dirs]
1085 list, then also remove the directory.
1086 * Update internal Unicode character tables, used in regular expression
1087 handling, from version 11.0 to 12.1.
1088 * In "[/help/regexp|fossil regexp]", "[/help/grep|fossil grep]"
1089 and the TH1 "regexp" command, the -nocase option now removes multiple
1090 diacritics from the same character (derived from SQLite's
1091 remove_diacritics=2)
1092 * Added the [/help/www/secureraw|/secureraw] page that requires the
1093 complete SHA1 or SHA3 hash, not just a prefix, before it will deliver
1094 content.
1095 * Accept purely numeric ISO8601 date/time strings as long as they
1096 do not conflict with a hash. Example: "20190510134217" instead of
1097 "2019-05-10 13:42:17". This helps keep URLs shorter and less
@@ -1040,19 +1104,19 @@
1104 extra path element is not needed.
1105 * If an automatic sync gets a permanent redirect request, then update
1106 the saved remote URL to the new address.
1107 * Temporary filenames (for example used for external "diff" commands)
1108 try to preserve the suffix of the original file.
1109 * Added the [/help/www/thisdayinhistory|/thisdayinhistory] web page.
1110 * Enhanced parsing of [/help/www/timeline|/timeline] query parameters
1111 "ymd=", "ym=", and "yw=". All arguments are option (in which case they
1112 default to the current time) and all accept ISO8601 date/times without
1113 punctuation.
1114 * Automatically disapprove pending moderation requests for a user when
1115 that user is deleted. This helps in dealing with spam-bots.
1116 * Improvements to the "Capability Summary" section in the
1117 [/help/www/secaudit0|Security Audit] web-page.
1118 * Use new "ci-lock" and "ci-lock-failed" pragmas in the
1119 [./sync.wiki|sync protocol] to try to prevent accident forks
1120 caused by concurrent commits when operating in auto-sync mode.
1121 * Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details])
1122 that can cause repository databases to be overwritten with debugging
@@ -1153,11 +1217,11 @@
1217 included in the header or footer.
1218 * Add the [./backoffice.md|backoffice].
1219 * Update internal Unicode character tables, used in regular expression
1220 handling, from version 10.0 to 11.0.
1221 * Improvements to the "Security Audit" administration page
1222 * Add the [/help/branch|fossil branch current] command.
1223 * Add the [./grep.md|grep] command.
1224 * Update the built-in SQLite to version 3.25.1.
1225 * Some code and interfaces are in place to support sending and
1226 receiving email directly via SMTP, but this feature is not yet
1227 complete or ready for production use.
@@ -1176,20 +1240,20 @@
1240 same as "Verbose" in the previous release. The "Verbose" mode is
1241 now like "Compact" except the extra check-in details are shown by
1242 default.
1243 * Add support for ETags:, Last-Modified:, and If-Modified-Since:
1244 cache control mechanisms.
1245 * Enhance the [/help/www/tarball|/tarball],
1246 [/help/www/zip|/zip], and
1247 [/help/www/sqlar|/sqlar] pages so that the checkin
1248 name to be downloaded can be expressed as part of the URI,
1249 and without the need for query parameters.
1250 * On the [/help/www/timeline|/timeline] webpage, add the days=N
1251 query parameter and enhance the ymd=DATE and yw=DATE query parameters
1252 to accept 'now' as an argument to show the latest day or week.
1253 * In the web page that comes up in response to the
1254 [/help/all|fossil all ui] command, show the last modification
1255 time for each repository, and allow click-to-sort on the modification
1256 time column.
1257 * In the tarball cache replacement algorithm, give extra weight to
1258 tarballs that have been accessed more than once.
1259 * Additional defenses against web-based attacks. There have not been
@@ -1236,35 +1300,35 @@
1300 to define their own URLs on the web interface that are rewritten to
1301 built-in URLs with specific parameters. Create and configure URL Aliases
1302 using the /Setup/URL_Aliases menu option in the web interface.
1303 * Add tech-note search capability.
1304 * Add the -r|--revision and -o|--origin options to the
1305 [/help/annotate|annotate] command.
1306 * Add the origin= query parameter to the [/help/www/annotate|/annotate]
1307 webpage.
1308 * The [/help/annotate|fossil annotate] command and the
1309 [/help/www/annotate|/annotate] web page go backwards in time as far
1310 as can be computed in 30 milliseconds by default, rather than stopping
1311 after 20 steps. The new limit= query parameter or the --limit command-line
1312 option can be used to alter this timeout.
1313 * Provide separate [/help#settings|on-line help screens for each setting].
1314 * Back out support for the --no-dir-symlinks option
1315 * Remove support from the legacy configuration sync protocol. The only
1316 way now to do a configuration push or pull is to use the new protocol that
1317 was added in 2011.
1318 * Add the from= and to= query parameters to [/help/www/fdiff|/fdiff]
1319 in order to get a diff of two files in the same check-in.
1320 * Fix the "ssh://" protocol to prevent an attack whereby the attacker convinces
1321 a victim to run a "clone" with a dodgy URL and thereby gains access to their
1322 system.
1323 * Provide a checkbox that will temporarily disable all ad-units.
1324 * Improvements to the [/help/www/stat|/stat] page
1325 * Various new hyperlinks to the [/help/www/bloblist|/bloblist]
1326 and [/help/www/bigbloblist|/bigbloblist] pages.
1327 * Correct the [/help/www/doc|/doc] page to support read-only repositories.
1328 * Correct [/help/www/zip|/zip], [/help/www/tarball|/tarball],
1329 [/help/zip|zip], and [/help/tarball|tarball] pages and commands to
1330 honor the versioned manifest setting when outside of an open checkout
1331 directory.
1332 * The admin-log and access-log settings are now on by default for
1333 new repositories.
1334 * Update the built-in SQLite to version 3.21.0.
@@ -1272,34 +1336,34 @@
1336 <h2 id='v2_3'>Changes for Version 2.3 (2017-07-21)</h2>
1337
1338 * Update the built-in SQLite to version 3.20.0 (beta).
1339 * Update internal Unicode character tables, used in regular expression
1340 handling, from version 9.0 to 10.0.
1341 * Show the last-sync-URL on the [/help/www/urllist|/urllist] page.
1342 * Added the "Event Summary" activity report.
1343 [/reports?type=ci&view=lastchng|example]
1344 * Added the "Security Audit" page, available to administrators only
1345 * Added the Last Login time to the user list page, for administrators only
1346 * Added the --numstat option to the [/help/diff|fossil diff] command
1347 * Limit the size of the heap and stack on unix systems, as a proactive
1348 defense against the
1349 [https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt|Stack Clash]
1350 attack.
1351 * Fix "database locked" warnings caused by "PRAGMA optimize".
1352 * Fix a potential XSS vulnerability on the
1353 [/help/www/help|/help] webpage.
1354 * Documentation updates
1355
1356 <h2 id='v2_2'>Changes for Version 2.2 (2017-04-11)</h2>
1357
1358 * GIT comment tags are now handled by Fossil during import/export.
1359 * Show the content of README files on directory listings.
1360 ([/file/skins|example])
1361 * Support for Basic Authentication if enabled (default off).
1362 * Show the hash algorithms used on the
1363 [/help/www/rcvfromlist|/rcvfromlist] page.
1364 * The [/help/www/tarball|/tarball] and [/help/www/zip|/zip] pages
1365 now use the the r= query parameter
1366 to select which check-in to deliver. The uuid= query parameter
1367 is still accepted for backwards compatibility.
1368 * Update the built-in SQLite to version 3.18.0.
1369 * Run "[https://www.sqlite.org/pragma.html#pragma_optimize|PRAGMA optimize]"
@@ -1308,12 +1372,12 @@
1372 <h2 id='v2_1'>Changes for Version 2.1 (2017-03-10)</h2>
1373
1374 * Add support for [./hashpolicy.wiki|hash policies] that control which
1375 of the Hardened-SHA1 or SHA3-256 algorithms is used to name new
1376 artifacts.
1377 * Add the "gshow" and "gcat" subcommands to [/help/stash|fossil stash].
1378 * Add the [/help/www/juvlist|/juvlist] web page and use it to construct
1379 the [/uv/download.html|Download Page] of the Fossil self-hosting website
1380 using Ajax.
1381
1382 <h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2>
1383
@@ -1321,23 +1385,23 @@
1385 [https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
1386 implementation by Marc Stevens and Dan Shumow.
1387 * Add the ability to read and understand
1388 [./fileformat.wiki#names|artifact names] that are based on SHA3-256
1389 rather than SHA1, but do not actually generate any such names.
1390 * Added the [/help/sha3sum|sha3sum] command.
1391 * Update the built-in SQLite to version 3.17.0.
1392
1393 <h2 id='v1_37'>Changes for Version 1.37 (2017-01-16)</h2>
1394
1395 * Add checkbox widgets to various web pages. See [/technote/8d18bf27e9|
1396 this technote] for more information. To get the checkboxes to look as
1397 intended, you must update the CSS in your repository and all clones.
1398 * Add the [/help/all|fossil all ui] command
1399 * Add the [/help/www/file|/file] webpage
1400 * Enhance the [/help/www/brlist|/brlist] webpage to make use of branch colors.
1401 * Add support for the ms=EXACT|LIKE|GLOB|REGEXP query parameter on the
1402 [/help/www/timeline|/timeline] webpage, with associated form widgets.
1403 * Enhance the [/help/changes|changes] and [/help/status|status] commands
1404 with many new filter options so that specific kinds of changes can be
1405 found without having to pipe through grep or sed.
1406 * Enhanced the [/help/sqlite3|fossil sql] command so that it opens the
1407 [./tech_overview.wiki#localdb|checkout database] and the
@@ -1350,11 +1414,11 @@
1414 </ul>
1415 * Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep
1416 crnl-glob as a compatibility alias.
1417 * Added the --command option to the [/help/diff|diff] command.
1418 * Fix a C99-ism that prevents the 1.36 release from building with MSVC.
1419 * Fix [/help/ticket|ticket set] when using the "+" prefix with fields
1420 from the "ticketchng" table.
1421 * Remove the "fusefs" command from builds that do not have the underlying
1422 support enabled.
1423 * Fixes for incremental git import/export.
1424 * Minor security enhancements to
@@ -1364,58 +1428,58 @@
1428
1429
1430 <h2 id='v1_36'>Changes for Version 1.36 (2016-10-24)</h2>
1431
1432 * Add support for [./unvers.wiki|unversioned content],
1433 the [/help/unversioned|fossil unversioned] command and the
1434 [/help/www/uv|/uv] and [/uvlist] web pages.
1435 * The [/uv/download.html|download page] is moved into
1436 [./unvers.wiki|unversioned content] so that the self-hosting Fossil
1437 websites no longer uses any external content.
1438 * Added the "Search" button to the graphical diff generated by
1439 the --tk option on the [/help/diff|diff] command.
1440 * Added the "--checkin VERSION" option to the
1441 [/help/diff|diff] command.
1442 * Various performance enhancements to the [/help/diff|diff] command.
1443 * Update internal Unicode character tables, used in regular expression
1444 handling, from version 8.0 to 9.0.
1445 * Update the built-in SQLite to version 3.15. Fossil now requires
1446 the SQLITE_DBCONFIG_MAINDBNAME interface of SQLite which is only available
1447 in SQLite version 3.15 and later and so Fossil will not work with
1448 earlier SQLite versions.
1449 * Fix [https://www.mail-archive.com/[email protected]/msg23618.html|multi-line timeline bug]
1450 * Enhance the [/help/purge|fossil purge] command.
1451 * New command [/help/shell|fossil shell].
1452 * SQL parameters whose names are all lower-case in Ticket Report SQL
1453 queries are filled in using HTTP query parameter values.
1454 * Added support for [./childprojects.wiki|child projects] that are
1455 able to pull from their parent but not push.
1456 * Added the -nocomplain option to the TH1 "query" command.
1457 * Added support for the chng=GLOBLIST query parameter on the
1458 [/help/www/timeline|/timeline] webpage.
1459
1460 <h2 id='v1_35'>Changes for Version 1.35 (2016-06-14)</h2>
1461
1462 * Enable symlinks by default on all non-Windows platforms.
1463 * Enhance the [/md_rules|Markdown formatting] so that hyperlinks that begin
1464 with "/" are relative to the root of the Fossil repository.
1465 * Rework the [/help/www/setup_ulist|/setup_list page] (the User List page)
1466 to display all users in a click-to-sort table.
1467 * Fix backslash-octal escape on filenames while importing from git
1468 * When markdown documents begin with &lt;h1&gt; HTML elements, use that
1469 header at the document title.
1470 * Added the [/help/www/bigbloblist|/bigbloblist page].
1471 * Enhance the [/help/www/finfo|/finfo page] so that when it is showing
1472 the ancestors of a particular file version, it only shows direct
1473 ancestors and omits changes on branches, thus making it show the same set
1474 of ancestors that are used for [/help/www/blame|/blame].
1475 * Added the --page option to the [/help/ui|fossil ui] command
1476 * Added the [/help/bisect|fossil bisect ui] command
1477 * Enhanced the [/help/diff|fossil diff] command so that it accepts
1478 directory names as arguments and computes diffs on all files contained
1479 within those directories.
1480 * Fix the [/help/add|fossil add] command so that it shows "SKIP" for
1481 files added that were already under management.
1482 * TH1 enhancements:
1483 <ul><li>Add <nowiki>[array exists]</nowiki> command.</li>
1484 <li>Add minimal <nowiki>[array names]</nowiki> command.</li>
1485 <li>Add tcl_platform(engine) and tcl_platform(platform) array
@@ -1423,32 +1487,32 @@
1487 </ul>
1488 * Get autosetup working with MinGW.
1489 * Fix autosetup detection of zlib in the source tree.
1490 * Added autosetup detection of OpenSSL when it may be present under the
1491 "compat" subdirectory of the source tree.
1492 * Added the [/help/reparent|fossil reparent] command
1493 * Added --include and --exclude options to [/help/tarball|fossil tarball]
1494 and [/help/zip|fossil zip] and the in= and ex= query parameters to the
1495 [/help/www/tarball|/tarball] and [/help/www/zip|/zip] web pages.
1496 * Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
1497 * If the FOSSIL_PWREADER environment variable is set, then use the program it
1498 names in place of getpass() to read passwords and passphrases
1499 * Option --baseurl now works on Windows.
1500 * Numerous documentation improvements.
1501 * Update the built-in SQLite to version 3.13.0.
1502
1503 <h2 id='v1_34'>Changes for Version 1.34 (2015-11-02)</h2>
1504
1505 * Make the [/help/clean|fossil clean] command undoable for files less
1506 than 10MiB.
1507 * Update internal Unicode character tables, used in regular expression
1508 handling, from version 7.0 to 8.0.
1509 * Add the new [/help/amend|amend] command which is used to modify
1510 tags of a "check-in".
1511 * Fix bug in [/help/import|import] command, handling version 3 of
1512 the svndump format for subversion.
1513 * Add the [/help/all|all cache] command.
1514 * TH1 enhancements:
1515 <ul><li>Add minimal <nowiki>[lsearch]</nowiki> command. Only exact
1516 case-sensitive matching is supported.</li>
1517 <li>Add the <nowiki>[glob_match]</nowiki>, <nowiki>[markdown]</nowiki>,
1518 <nowiki>[dir]</nowiki>, and <nowiki>[encode64]</nowiki> commands.</li>
@@ -1455,106 +1519,106 @@
1519 <li>Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> commands to
1520 the Tcl integration subsystem.</li>
1521 <li>Add 'double', 'integer', and 'list' classes to the
1522 <nowiki>[string is]</nowiki> command.</li>
1523 </ul>
1524 * Add the --undo option to the [/help/diff|diff] command.
1525 * Build-in Antirez's "linenoise" command-line editing library for use with
1526 the [/help/sqlite3|fossil sql] command on Unix platforms.
1527 * Add [/help/stash|stash cat] as an alias for the
1528 [/help/stash|stash show] command.
1529 * Automatically pull before [/help/merge|fossil merge] when auto-sync
1530 is enabled.
1531 * Fix --hard option to [/help/mv|fossil mv] and [/help/rm|fossil rm]
1532 to enable them to work properly with certain relative paths.
1533 * Change the mimetype for ".n" and ".man" files to text/plain.
1534 * Display improvements in the [/help/bisect|fossil bisect chart] command.
1535 * Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5
1536 support (both currently unused within Fossil).
1537
1538 <h2 id='v1_33'>Changes for Version 1.33 (2015-05-23)</h2>
1539 * Improved fork detection on [/help/update|fossil update],
1540 [/help/status|fossil status] and related commands.
1541 * Change the default skin to what used to be called "San Francisco Modern".
1542 * Add the [/repo-tabsize] web page
1543 * Add [/help/import|fossil import --svn], for importing a subversion
1544 repository into fossil which was exported using "svnadmin dump".
1545 * Add the "--compress-only" option to [/help/rebuild|fossil rebuild].
1546 * Use a pie chart on the [/reports?view=byuser] page.
1547 * Enhanced [/help/clean|fossil clean --verily] so that it ignores
1548 keep-glob and ignore-glob settings. Added the -x alias for --verily.
1549 * Add the --soft and --hard options to [/help/rm|fossil rm] and
1550 [/help/mv|fossil mv]. The default is still --soft, but that is
1551 now configurable at compile-time or by the mv-rm-files setting.
1552 * Improved ability to [./customgraph.md|customize the timeline graph].
1553 * Improvements to the [/sitemap] page.
1554 * Automatically adjust the [/help/timeline|CLI timeline] to the terminal
1555 width on Linux.
1556 * Added <nowiki>[info commands] and [info vars]</nowiki> commands to TH1.
1557 These commands perform the same function as their Tcl counterparts,
1558 except they do not accept a pattern argument.
1559 * Fix some obscure issues with TH1 expression processing.
1560 * Fix titles in search results for documents that are not wiki, markdown,
1561 or HTML.
1562 * Formally translate TH1 to Tcl return codes and vice-versa, where
1563 necessary, in the Tcl integration subsystem.
1564 * Add [/help/leaves|fossil leaves -multiple], for finding multiple
1565 leaves on the same branch.
1566 * Added the "Blitz" skin option.
1567 * Removed the ".fossil-settings/keep-glob" file. It should not have been
1568 checked into the repository.
1569 * Update the built-in SQLite to version 3.8.10.2.
1570 * Make [/help/open|fossil open] honor ".fossil-settings/allow-symlinks".
1571 * Allow [/help/add|fossil add] to be used on symlinks to nonexistent or
1572 unreadable files in the same way as [/help/addremove|fossil addremove].
1573 * Added fork warning to be issued if sync produced a fork
1574 * Update the [/help/www/info|info] page to report when a file becomes a
1575 symlink. Additionally show the UUID for files whose types have changed
1576 without changing contents or symlink target.
1577 * Have [/help/changes|fossil changes] and
1578 [/help/status|fossil status] report when executable or symlink status
1579 changes on otherwise unmodified files.
1580 * Permit filtering weekday and file [/help/www/reports|reports] by user.
1581 Also ensure the user parameter is preserved when changing types. Add a
1582 field for direct entry of the user name to each applicable report.
1583 * Create parent directories of [/help/settings|empty-dirs] if they don't
1584 already exist.
1585 * Inhibit timeline links to wiki pages that have been deleted.
1586
1587 <h2 id='v1_33'>Changes for Version 1.32 (2015-03-14)</h2>
1588 * When creating a new repository using [/help/init|fossil init], ensure
1589 that the new repository is fully compatible with historical versions of
1590 Fossil by having a valid manifest as RID 1.
1591 * Anti-aliased rendering of arrowheads on timeline graphs.
1592 * Added vi/less-style key bindings to the --tk diff GUI.
1593 * Documentation updates to fix spellings and changes all "checkins" to
1594 "check-ins".
1595 * Add the --repolist option to server commands such as
1596 [/help/server|fossil server] or [/help/http|fossil http].
1597 * Added the "Xekri" skin.
1598 * Enhance the "ln=" query parameter on artifact displays to accept multiple
1599 ranges, separate by spaces (or "+" when URL-encoded).
1600 * Added [/help/forget|fossil forget] as an alias for
1601 [/help/rm|fossil rm].
1602
1603 <h2 id='v1_31'>Changes For Version 1.31 (2015-02-23)</h2>
1604 * Change the auxiliary schema by adding columns MLINK.ISAUX and MLINK.PMID
1605 columns to the schema, to support better drawing of file change graphs.
1606 A [/help/rebuild|fossil rebuild] is recommended but is not required.
1607 so that the new graph drawing logic can work effectively.
1608 * Added [/search|search] over Check-in comments, Documents, Tickets and
1609 Wiki. Disabled by default. The search can be either a full-scan or it
1610 can use an index that is kept up-to-date automatically. The new
1611 /srchsetup web-page and the [/help/fts-config|fts-config] command
1612 were added to help configure the search capability. Expect further
1613 enhancements to the search capabilities in subsequent releases.
1614 * Added form elements to some submenus (in particular the /timeline)
1615 for easier operation.
1616 * Added the --ifneeded option to [/help/rebuild|fossil rebuild].
1617 * Added "override skins" using the "skin:" line of the CGI script or
1618 using the --skin LABEL option on the [/help/server|server],
1619 [/help/ui|ui], or [/help/http|http] commands.
1620 * Embedded html documents that begin with
1621 &lt;doc class="fossil-doc"&gt; are displayed with standard
1622 headers and footers added.
1623 * Allow &lt;div style='...'&gt; markup in [/wiki_rules|wiki].
1624 * Renamed "Events" to "Technical Notes", while updating the technote
@@ -1561,24 +1625,24 @@
1625 display and control pages. Add support for technotes as plain text
1626 or as Markdown.
1627 * Added the [/md_rules] pages containing summary instructions on the
1628 Markdown format.
1629 * Added the --repolist and --nojail options to the various server commands
1630 (ex: [/help/server|fossil server]).
1631 * Added the [/help/all|fossil all add] subcommand to "fossil all".
1632 * Improvements to the /login page. Some hyperlinks to pages that require
1633 "anonymous" privileges are displayed even if the current user is "nobody"
1634 but automatically redirect to /login.
1635 * The [/help/www/doc|/doc] web-page will now try to deliver the file
1636 "404.md" from the top-level directory (if such a file exists) in
1637 place of its built-in 404 text.
1638 * Download of Tarballs and ZIP Archives by user "nobody" is now enabled
1639 by default in new repositories.
1640 * Enhancements to the table sorting controls. More display tables
1641 are now sortable.
1642 * Add IPv6 support to [/help/sync|fossil sync] and
1643 [/help/clone|fossil clone]
1644 * Add more skins such as "San Francisco Modern" and "Eagle".
1645 * During shutdown, check to see if the check-out database (".fslckout")
1646 contains a lot of free space, and if it does, VACUUM it.
1647 * Added the [/mimetype_list] page.
1648 * Added the [/hash-collisions] page.
@@ -1586,14 +1650,14 @@
1650 ticket reports.
1651 * Break out the components (css, footer, and header) for the
1652 various built-in skins into separate files in the source tree.
1653
1654 <h2 id='v1_30'>Changes For Version 1.30 (2015-01-19)</h2>
1655 * Added the [/help/bundle|fossil bundle] command.
1656 * Added the [/help/purge|fossil purge] command.
1657 * Added the [/help/publish|fossil publish] command.
1658 * Added the [/help/unpublished|fossil unpublished] command.
1659 * Enhance the [/tree] webpage to show the age of each file with the option
1660 to sort by age.
1661 * Enhance the [/brlist] webpage to show additional information about each branch
1662 and to be sortable by clicking on column headers.
1663 * Add support for Docker. Just install docker and type
@@ -1603,17 +1667,17 @@
1667 copies of all historical check-ins. This only works on systems that
1668 support FuseFS.
1669 * Add the administrative log that records all configuration.
1670 * Added the [/sitemap] webpage.
1671 * Added the [/bloblist] web page.
1672 * Let [/help/new|fossil new] no longer create an initial empty commit
1673 by default. The first commit after checking out an empty repository will
1674 become the initial commit.
1675 * Added the [/help/all|fossil all dbstat] and
1676 [/help/all|fossil all info] commands.
1677 * Update SQLite to version 3.8.8.
1678 * Added the --verily option to the [/help/clean|fossil clean] command.
1679 * Add the "autosync-tries" setting to control the number of autosync attempts
1680 before returning an error.
1681 * Added a compile-time option (--with-miniz) to build using miniz instead
1682 of zlib. Disabled by default.
1683 * Support customization of commands and webpages, including the ability to
@@ -1623,12 +1687,12 @@
1687 [trace], [getParameter], [setParameter], [artifact], and
1688 [globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks.
1689 * Automatically adjust the width of command-line timeline output according to the
1690 detected width of the terminal.
1691 * Prompt the user to optionally fix invalid UTF-8 at check-in.
1692 * Added a line-number toggle option to the [/help/www/info|/info]
1693 and [/help/www/artifact|/artifact] pages.
1694 * Most commands now issue errors rather than silently ignoring unrecognized
1695 command-line options.
1696 * Use full 40-character SHA1 hashes (instead of abbreviations) in most
1697 internal URLs.
1698 * The "ssh:" sync method on Windows now uses "plink.exe" instead of "ssh" as
@@ -1642,11 +1706,11 @@
1706 * Fix a rare and long-standing sync protocol bug
1707 that would silently prevent the sync from running to completion. Before
1708 this bug-fix it was sometimes necessary to do "fossil sync --verily" to
1709 get two repositories in sync.
1710 * Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful
1711 for ad hoc queries in the [/help/sqlite3|fossil sql] interface,
1712 and also used internally.
1713 * Added the "$secureurl" TH1 variable for use in headers and footers.
1714 * (Internal:) Add the ability to include resources as separate files in the
1715 source tree that are converted into constant byte arrays in the compiled
1716 binary. Use this feature to store the Tk script that implements the --tk
@@ -1666,143 +1730,143 @@
1730 * The [/reports] page now requires Read ("o") permissions. The "byweek"
1731 report now properly propagates the selected year through the event type
1732 filter links.
1733 * The [/help/info | info command] now shows leaf status of the checkout.
1734 * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
1735 * Add option --empty to the "[/help/open | fossil open]" command.
1736 * Enhanced [/help/www/fileage|the fileage page] to support a glob parameter.
1737 * Add -w|--ignore-all-space and -Z|--ignore-trailing-space options to
1738 [/help/annotate|fossil annotate], [/help/blame|fossil blame],
1739 [/help/diff|fossil (g)diff], [/help/stash|fossil stash diff].
1740 * Add --strip-trailing-cr option to [/help/diff|fossil (g)diff] and
1741 [/help/stash|fossil stash diff].
1742 * Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
1743 and /vdiff UI pages.
1744 * Enhance [/reports?view=byweekday|/reports] with a "byweekday" view.
1745 * Enhance the [/help/cat|fossil cat] command so that it works outside
1746 of a checkout when using the -R command-line option.
1747 * Use full-length SHA1 hashes, not abbreviations, in most hyperlinks.
1748 * Correctly render the &lt;title&gt; markup on wiki pages in the
1749 [/help/www/artifact|/artifact] webpage.
1750 * Enhance the [/help/whatis|fossil whatis] command to report on attachments
1751 and cluster artifacts. Added the [/help/test-whatis-all] command for
1752 testing purposes.
1753 * Add support for HTTP Basic Authentication on [/help/clone|clone] and
1754 [/help/sync|sync].
1755 * Fix the [/help/stash|stash] so that it remembers added files and re-adds
1756 them when the stash is applied.
1757 * Fix the server so that it avoids writing to the database (and thus avoids
1758 database locking issues) on a
1759 [/help/pull|pull] or [/help/clone|clone].
1760 * Add support for [./server.wiki#loadmgmt|server load management] using both
1761 a cache of expensive pages (the [/help/cache|fossil cache] command)
1762 and by rejecting expensive page requests when the server load average is too
1763 high.
1764 * Add the [/help/praise|fossil praise] command as an alias for
1765 [/help/blame|fossil blame] for subversion compatibility.
1766 * Enhance the [/help/test-diff|fossil test-diff] command with -y or --tk
1767 options so that it shows both filenames above their respective columns in
1768 the side-by-side diff output.
1769 * Issue a warning if a [/help/add|fossil add] command tries to add a file
1770 that matches the ignore-glob.
1771 * Add option -W|--width to "[/help/stash|fossil stash ls]"
1772 and "[/help/leaves|fossil leaves]" commands.
1773 * Enhance support for running as the root user. Now works on Haiku.
1774 * Added the <tt>-empty</tt> option to [/help/new|fossil new], which
1775 causes it to not create an initial empty commit. The first commit after
1776 checking out a repo created this way will become the initial commit.
1777 * Enhance sync operations by committing each round-trip to minimize number
1778 of retransmits when autosync fails. Include option for
1779 [/help/update| fossil update] and [/help/merge| fossil merge] to
1780 continue even if missing content.
1781 * Minor portability fixes for platforms where the char type is unsigned
1782 by default.
1783
1784 <h2>Changes For Version 1.28 (2014-01-27)</h2>
1785 * Enhance [/help/www/reports | /reports] to support event type filtering.
1786 * When cloning a repository, the user name passed via the URL (if any)
1787 is now used as the default local admin user's name.
1788 * Enhance the SSH transport mechanism so that it runs a single instance of
1789 the "fossil" executable on the remote side, obviating the need for a shell
1790 on the remote side. Some users may need to add the "?fossil=/path/to/fossil"
1791 query parameter to "ssh:" URIs if their fossil binary is not in a standard
1792 place.
1793 * Add the "[/help/blame | fossil blame]" command that works just like
1794 "fossil annotate" but uses a different output format that includes the
1795 user who made each change and omits line numbers.
1796 * Add the "Tarball and ZIP-archive Prefix" configuration parameter under
1797 Admin/Configuration.
1798 * Fix CGI processing so that it works on web servers that do not
1799 supply REQUEST_URI.
1800 * Add options --dirsonly, --emptydirs, and --allckouts to the
1801 "[/help/clean | fossil clean]" command.
1802 * Ten-fold performance improvement in large "fossil blame" or
1803 "fossil annotate" commands.
1804 * Add option -W|--width and --offset to "[/help/timeline | fossil timeline]"
1805 and "[/help/finfo | fossil finfo]" commands.
1806 * Option -n|--limit of "[/help/timeline | fossil timeline]" now
1807 specifies the number of entries, just like all other commands which
1808 have the -n|--limit option. The various timeline-related functions
1809 now output "--- ?? limit (??) reached ---" at the end whenever
1810 appropriate. Use "-n 0" if no limit is desired.
1811 * Fix handling of password embedded in Fossil URL.
1812 * New <tt>--once</tt> option to [/help/clone | fossil clone] command
1813 which does not store the URL or password when cloning.
1814 * Modify [/help/ui | fossil ui] to respect "default user" in an open
1815 repository.
1816 * Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
1817 * Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
1818 * Advanced possibilities for commit and ticket change notifications over
1819 http using TH1 scripting.
1820 * Add --sha1sum and --integrate options
1821 to the "[/help/commit | fossil commit]" command.
1822 * Add the "clean" and "extra" subcommands to the
1823 "[/help/all | fossil all]" command
1824 * Add the --whatif option to "[/help/clean|fossil clean]" that works the
1825 same as "--dry-run",
1826 so that the name does not collide with the --dry-run option of "fossil all".
1827 * Provide a configuration option to show dates on the web timeline
1828 as "YYMMMDD HH:MM"
1829 * Add an option to the "stats" webpage that allows an administrator to see
1830 the current repository schema.
1831 * Enhancements to the "[/help/www/vdiff|/vdiff]" webpage for more difference
1832 display options.
1833 * Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative
1834 to "/dir" and make it the default way of showing file lists.
1835 * Send gzipped HTTP responses to clients that support it.
1836
1837 <h2>Changes For Version 1.27 (2013-09-11)</h2>
1838 * Enhance the [/help/changes | fossil changes],
1839 [/help/clean | fossil clean], [/help/extras | fossil extras],
1840 [/help/ls | fossil ls] and [/help/status | fossil status] commands
1841 to restrict operation to files and directories named on the command-line.
1842 * New --integrate option to [/help/merge | fossil merge], which
1843 automatically closes the merged branch when committing.
1844 * Renamed <tt>/stats_report</tt> page to [/reports]. Graph width is now
1845 relative, not absolute.
1846 * Added <tt>yw=YYYY-WW</tt> (year-week) filter to timeline to limit the results
1847 to a specific year and calendar week number, e.g. [/timeline?yw=2013-01].
1848 * Updates to SQLite to prevent opening a repository file using file descriptors
1849 1 or 2 on Unix. This fixes a bug under which an assertion failure could
1850 overwrite part of a repository database file, corrupting it.
1851 * Added support for unlimited line lengths in side-by-side diffs.
1852 * New --close option to [/help/commit | fossil commit], which
1853 immediately closes the branch being committed.
1854 * Added <tt>chart</tt> option to [/help/bisect | fossil bisect].
1855 * Improvements to the "human or bot?" determination.
1856 * Reports errors about missing CGI-standard environment variables for HTTP
1857 servers which do not support them.
1858 * Minor improvements to sync support on Windows.
1859 * Added <tt>--scgi</tt> option to [/help/server | fossil server].
1860 * Internal improvements to the sync process.
1861 * The internals of the JSON API are now MIT-licensed, so downstream
1862 users/packagers are no longer affected by the "do no evil" license
1863 clause.
1864
1865 <h2>Changes For Version 1.26 (2013-06-18)</h2>
1866 * The argument to the --port option for the [/help/ui | fossil ui] and
1867 [/help/server | fossil server] commands can take an IP address in addition
1868 to the port number, causing Fossil to bind to just that one IP address.
1869 * After prompting for a password, also ask if that password should be
1870 remembered.
1871 * Performance improvements to the diff engine.
1872 * Fix the side-by-side diff engine to work better with multi-byte Unicode text.
@@ -1809,11 +1873,11 @@
1873 * Color-coding in the web-based annotation (blame) display. Fix the annotation
1874 engine so that it is no longer confused by time-warps.
1875 * The markdown formatter is now available by default and can be used for
1876 tickets, wiki, and embedded documentation.
1877 * Add subcommands "fossil bisect log" and "fossil bisect status" to the
1878 [/help/bisect | fossil bisect] command, as well as other bisect enhancements.
1879 * Enhanced defenses that prevent spiders from using excessive CPU and bandwidth.
1880 * Consistent use of the -n or --dry-run command line options.
1881 * Win32: Fossil now understands Cygwin paths containing one or more of
1882 the characters <nowiki>"*:<>?|</nowiki>. Those are normally forbidden in
1883 win32. This means that the win32 fossil.exe is better usable in a Cygwin
1884
+8 -8
--- www/chat.md
+++ www/chat.md
@@ -55,11 +55,11 @@
5555
from the repository database.
5656
5757
## <a id="usage"></a>Usage
5858
5959
For users with appropriate permissions, simply browse to the
60
-[/chat](/help?cmd=/chat) to start up a chat session. The default
60
+[/chat](/help/www/chat) to start up a chat session. The default
6161
skin includes a "Chat" entry on the menu bar on wide screens for
6262
people with chat privilege. There is also a "Chat" option on
6363
the [Sitemap page](/sitemap), which means that chat will appear
6464
as an option under the hamburger menu for many [skins](./customskin.md).
6565
@@ -122,19 +122,19 @@
122122
specific user by tapping on that user's name, tapping a second time to
123123
remove the filter.
124124
125125
### <a id="cli"></a> The `fossil chat` Command
126126
127
-Type [fossil chat](/help?cmd=chat) from within any open check-out
127
+Type [fossil chat](/help/chat) from within any open check-out
128128
to bring up a chatroom for the project that is in that checkout.
129129
The new chat window will attempt to connect to the default sync
130130
target for that check-out (the server whose URL is shown by the
131
-[fossil remote](/help?cmd=remote) command).
131
+[fossil remote](/help/remote) command).
132132
133133
### <a id="robots"></a> Chat Messages From Robots
134134
135
-The [fossil chat send](/help?cmd=chat) can be used by project-specific
135
+The [fossil chat send](/help/chat) can be used by project-specific
136136
robots to send notifications to the chatroom. For example, on the
137137
[SQLite project](https://sqlite.org/) (for which the Fossil chatroom
138138
feature, and indeed all of Fossil, was invented) there are long-running
139139
fuzz servers that sometimes run across obscure problems. Whenever this
140140
happens, a message is sent to the SQLite developers chatroom alerting
@@ -155,11 +155,11 @@
155155
Substitute the appropriate project URL, robot account
156156
name and password, message text and file attachment, of course.
157157
158158
### <a id="chat-robot"></a> Chat Messages For Timeline Events
159159
160
-If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not an
160
+If the [chat-timeline-user setting](/help/chat-timeline-user) is not an
161161
empty string, then any change to the repository that would normally result
162162
in a new timeline entry is announced in the chatroom. The announcement
163163
appears to come from a user whose name is given by the chat-timeline-user
164164
setting.
165165
@@ -189,11 +189,11 @@
189189
190190
*You do not need to understand how Fossil chat works in order to use it.
191191
But many developers prefer to know how their tools work.
192192
This section is provided for the benefit of those curious developers.*
193193
194
-The [/chat](/help?cmd=/chat) webpage downloads a small amount of HTML
194
+The [/chat](/help/www/chat) webpage downloads a small amount of HTML
195195
and a small amount of javascript to run the chat session. The
196196
javascript uses XMLHttpRequest (XHR) to download chat content, post
197197
new content, or delete historical messages. The following web
198198
interfaces are used by the XHR:
199199
@@ -246,17 +246,17 @@
246246
~~~
247247
248248
The CHAT table is not cross-linked with any other tables in the repository
249249
schema. An administrator can "DROP TABLE chat;" at any time, without
250250
harm (apart from deleting all chat history, of course). The CHAT table
251
-is dropped when running [fossil scrub --verily](/help?cmd=scrub).
251
+is dropped when running [fossil scrub --verily](/help/scrub).
252252
253253
On the server-side, message text is stored exactly as entered by the
254254
users. The /chat-poll page queries the CHAT table and constructs a
255255
JSON reply described in the [/chat-poll
256
-documentation](/help?cmd=/chat-poll). The message text is translated
256
+documentation](/help/www/chat-poll). The message text is translated
257257
into HTML before being converted to JSON so that the text can be
258258
safely added to the display using assignment to `innerHTML`. Though
259259
`innerHTML` assignment is generally considered unsafe, it is only so
260260
with untrusted content from untrusted sources. The chat content goes
261261
through sanitization steps which eliminate any potential security
262262
vulnerabilities of assigning that content to `innerHTML`.
263263
--- www/chat.md
+++ www/chat.md
@@ -55,11 +55,11 @@
55 from the repository database.
56
57 ## <a id="usage"></a>Usage
58
59 For users with appropriate permissions, simply browse to the
60 [/chat](/help?cmd=/chat) to start up a chat session. The default
61 skin includes a "Chat" entry on the menu bar on wide screens for
62 people with chat privilege. There is also a "Chat" option on
63 the [Sitemap page](/sitemap), which means that chat will appear
64 as an option under the hamburger menu for many [skins](./customskin.md).
65
@@ -122,19 +122,19 @@
122 specific user by tapping on that user's name, tapping a second time to
123 remove the filter.
124
125 ### <a id="cli"></a> The `fossil chat` Command
126
127 Type [fossil chat](/help?cmd=chat) from within any open check-out
128 to bring up a chatroom for the project that is in that checkout.
129 The new chat window will attempt to connect to the default sync
130 target for that check-out (the server whose URL is shown by the
131 [fossil remote](/help?cmd=remote) command).
132
133 ### <a id="robots"></a> Chat Messages From Robots
134
135 The [fossil chat send](/help?cmd=chat) can be used by project-specific
136 robots to send notifications to the chatroom. For example, on the
137 [SQLite project](https://sqlite.org/) (for which the Fossil chatroom
138 feature, and indeed all of Fossil, was invented) there are long-running
139 fuzz servers that sometimes run across obscure problems. Whenever this
140 happens, a message is sent to the SQLite developers chatroom alerting
@@ -155,11 +155,11 @@
155 Substitute the appropriate project URL, robot account
156 name and password, message text and file attachment, of course.
157
158 ### <a id="chat-robot"></a> Chat Messages For Timeline Events
159
160 If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not an
161 empty string, then any change to the repository that would normally result
162 in a new timeline entry is announced in the chatroom. The announcement
163 appears to come from a user whose name is given by the chat-timeline-user
164 setting.
165
@@ -189,11 +189,11 @@
189
190 *You do not need to understand how Fossil chat works in order to use it.
191 But many developers prefer to know how their tools work.
192 This section is provided for the benefit of those curious developers.*
193
194 The [/chat](/help?cmd=/chat) webpage downloads a small amount of HTML
195 and a small amount of javascript to run the chat session. The
196 javascript uses XMLHttpRequest (XHR) to download chat content, post
197 new content, or delete historical messages. The following web
198 interfaces are used by the XHR:
199
@@ -246,17 +246,17 @@
246 ~~~
247
248 The CHAT table is not cross-linked with any other tables in the repository
249 schema. An administrator can "DROP TABLE chat;" at any time, without
250 harm (apart from deleting all chat history, of course). The CHAT table
251 is dropped when running [fossil scrub --verily](/help?cmd=scrub).
252
253 On the server-side, message text is stored exactly as entered by the
254 users. The /chat-poll page queries the CHAT table and constructs a
255 JSON reply described in the [/chat-poll
256 documentation](/help?cmd=/chat-poll). The message text is translated
257 into HTML before being converted to JSON so that the text can be
258 safely added to the display using assignment to `innerHTML`. Though
259 `innerHTML` assignment is generally considered unsafe, it is only so
260 with untrusted content from untrusted sources. The chat content goes
261 through sanitization steps which eliminate any potential security
262 vulnerabilities of assigning that content to `innerHTML`.
263
--- www/chat.md
+++ www/chat.md
@@ -55,11 +55,11 @@
55 from the repository database.
56
57 ## <a id="usage"></a>Usage
58
59 For users with appropriate permissions, simply browse to the
60 [/chat](/help/www/chat) to start up a chat session. The default
61 skin includes a "Chat" entry on the menu bar on wide screens for
62 people with chat privilege. There is also a "Chat" option on
63 the [Sitemap page](/sitemap), which means that chat will appear
64 as an option under the hamburger menu for many [skins](./customskin.md).
65
@@ -122,19 +122,19 @@
122 specific user by tapping on that user's name, tapping a second time to
123 remove the filter.
124
125 ### <a id="cli"></a> The `fossil chat` Command
126
127 Type [fossil chat](/help/chat) from within any open check-out
128 to bring up a chatroom for the project that is in that checkout.
129 The new chat window will attempt to connect to the default sync
130 target for that check-out (the server whose URL is shown by the
131 [fossil remote](/help/remote) command).
132
133 ### <a id="robots"></a> Chat Messages From Robots
134
135 The [fossil chat send](/help/chat) can be used by project-specific
136 robots to send notifications to the chatroom. For example, on the
137 [SQLite project](https://sqlite.org/) (for which the Fossil chatroom
138 feature, and indeed all of Fossil, was invented) there are long-running
139 fuzz servers that sometimes run across obscure problems. Whenever this
140 happens, a message is sent to the SQLite developers chatroom alerting
@@ -155,11 +155,11 @@
155 Substitute the appropriate project URL, robot account
156 name and password, message text and file attachment, of course.
157
158 ### <a id="chat-robot"></a> Chat Messages For Timeline Events
159
160 If the [chat-timeline-user setting](/help/chat-timeline-user) is not an
161 empty string, then any change to the repository that would normally result
162 in a new timeline entry is announced in the chatroom. The announcement
163 appears to come from a user whose name is given by the chat-timeline-user
164 setting.
165
@@ -189,11 +189,11 @@
189
190 *You do not need to understand how Fossil chat works in order to use it.
191 But many developers prefer to know how their tools work.
192 This section is provided for the benefit of those curious developers.*
193
194 The [/chat](/help/www/chat) webpage downloads a small amount of HTML
195 and a small amount of javascript to run the chat session. The
196 javascript uses XMLHttpRequest (XHR) to download chat content, post
197 new content, or delete historical messages. The following web
198 interfaces are used by the XHR:
199
@@ -246,17 +246,17 @@
246 ~~~
247
248 The CHAT table is not cross-linked with any other tables in the repository
249 schema. An administrator can "DROP TABLE chat;" at any time, without
250 harm (apart from deleting all chat history, of course). The CHAT table
251 is dropped when running [fossil scrub --verily](/help/scrub).
252
253 On the server-side, message text is stored exactly as entered by the
254 users. The /chat-poll page queries the CHAT table and constructs a
255 JSON reply described in the [/chat-poll
256 documentation](/help/www/chat-poll). The message text is translated
257 into HTML before being converted to JSON so that the text can be
258 safely added to the display using assignment to `innerHTML`. Though
259 `innerHTML` assignment is generally considered unsafe, it is only so
260 with untrusted content from untrusted sources. The chat content goes
261 through sanitization steps which eliminate any potential security
262 vulnerabilities of assigning that content to `innerHTML`.
263
--- www/childprojects.wiki
+++ www/childprojects.wiki
@@ -47,11 +47,11 @@
4747
4848
The child project and the parent project will not normally be able to sync
4949
with one another, since they are now separate projects with distinct
5050
project codes. However, if the
5151
"--from-parent-project" command-line option is provided to the
52
-"[/help?cmd=pull|fossil pull]" command in the child, and the URL of
52
+"[/help/pull|fossil pull]" command in the child, and the URL of
5353
parent repository is also provided on the command-line, then updates to
5454
the parent project that occurred after the child was created will be added
5555
to the child repository. Thus, by periodically doing a
5656
pull --from-parent-project, the child project is able to stay up to date
5757
with all the latest changes in the parent.
5858
--- www/childprojects.wiki
+++ www/childprojects.wiki
@@ -47,11 +47,11 @@
47
48 The child project and the parent project will not normally be able to sync
49 with one another, since they are now separate projects with distinct
50 project codes. However, if the
51 "--from-parent-project" command-line option is provided to the
52 "[/help?cmd=pull|fossil pull]" command in the child, and the URL of
53 parent repository is also provided on the command-line, then updates to
54 the parent project that occurred after the child was created will be added
55 to the child repository. Thus, by periodically doing a
56 pull --from-parent-project, the child project is able to stay up to date
57 with all the latest changes in the parent.
58
--- www/childprojects.wiki
+++ www/childprojects.wiki
@@ -47,11 +47,11 @@
47
48 The child project and the parent project will not normally be able to sync
49 with one another, since they are now separate projects with distinct
50 project codes. However, if the
51 "--from-parent-project" command-line option is provided to the
52 "[/help/pull|fossil pull]" command in the child, and the URL of
53 parent repository is also provided on the command-line, then updates to
54 the parent project that occurred after the child was created will be added
55 to the child repository. Thus, by periodically doing a
56 pull --from-parent-project, the child project is able to stay up to date
57 with all the latest changes in the parent.
58
+1 -1
--- www/chroot.md
+++ www/chroot.md
@@ -30,11 +30,11 @@
3030
[configured Fossil with `--static`][bld] to avoid it
3131
3232
Fossil does all of this as one of many layers of defense against
3333
hacks and exploits. You can prevent Fossil from entering the chroot
3434
jail using the <tt>--nojail</tt> option to the
35
-[fossil server command](/help?cmd=server)
35
+[fossil server command](/help/server)
3636
but you cannot make Fossil hold onto root privileges. Fossil always drops
3737
root privilege before accepting inputs, for security.
3838
3939
4040
[bld]: https://fossil-scm.org/home/doc/trunk/www/build.wiki
4141
--- www/chroot.md
+++ www/chroot.md
@@ -30,11 +30,11 @@
30 [configured Fossil with `--static`][bld] to avoid it
31
32 Fossil does all of this as one of many layers of defense against
33 hacks and exploits. You can prevent Fossil from entering the chroot
34 jail using the <tt>--nojail</tt> option to the
35 [fossil server command](/help?cmd=server)
36 but you cannot make Fossil hold onto root privileges. Fossil always drops
37 root privilege before accepting inputs, for security.
38
39
40 [bld]: https://fossil-scm.org/home/doc/trunk/www/build.wiki
41
--- www/chroot.md
+++ www/chroot.md
@@ -30,11 +30,11 @@
30 [configured Fossil with `--static`][bld] to avoid it
31
32 Fossil does all of this as one of many layers of defense against
33 hacks and exploits. You can prevent Fossil from entering the chroot
34 jail using the <tt>--nojail</tt> option to the
35 [fossil server command](/help/server)
36 but you cannot make Fossil hold onto root privileges. Fossil always drops
37 root privilege before accepting inputs, for security.
38
39
40 [bld]: https://fossil-scm.org/home/doc/trunk/www/build.wiki
41
--- www/ckout-workflows.md
+++ www/ckout-workflows.md
@@ -135,8 +135,8 @@
135135
136136
The `/repo` addition is the key: whatever comes after is used as the
137137
repository name. [See the docs][clone] for more details.
138138
139139
[caod]: https://fossil-scm.org/forum/forumpost/3f143cec74
140
-[clone]: /help?cmd=clone
140
+[clone]: /help/clone
141141
142142
<div style="height:50em" id="this-space-intentionally-left-blank"></div>
143143
--- www/ckout-workflows.md
+++ www/ckout-workflows.md
@@ -135,8 +135,8 @@
135
136 The `/repo` addition is the key: whatever comes after is used as the
137 repository name. [See the docs][clone] for more details.
138
139 [caod]: https://fossil-scm.org/forum/forumpost/3f143cec74
140 [clone]: /help?cmd=clone
141
142 <div style="height:50em" id="this-space-intentionally-left-blank"></div>
143
--- www/ckout-workflows.md
+++ www/ckout-workflows.md
@@ -135,8 +135,8 @@
135
136 The `/repo` addition is the key: whatever comes after is used as the
137 repository name. [See the docs][clone] for more details.
138
139 [caod]: https://fossil-scm.org/forum/forumpost/3f143cec74
140 [clone]: /help/clone
141
142 <div style="height:50em" id="this-space-intentionally-left-blank"></div>
143
+2 -2
--- www/co-vs-up.md
+++ www/co-vs-up.md
@@ -24,8 +24,8 @@
2424
2525
In summary, these are two separate commands; neither is an alias for the
2626
other. They overlap enough that they can be used interchangeably for
2727
some use cases, but `update` is more powerful and more broadly useful.
2828
29
-[co]: /help?cmd=checkout
29
+[co]: /help/checkout
3030
[cvsmu]: http://web.mit.edu/gnu/doc/html/cvs_7.html#SEC37
31
-[up]: /help?cmd=update
31
+[up]: /help/update
3232
--- www/co-vs-up.md
+++ www/co-vs-up.md
@@ -24,8 +24,8 @@
24
25 In summary, these are two separate commands; neither is an alias for the
26 other. They overlap enough that they can be used interchangeably for
27 some use cases, but `update` is more powerful and more broadly useful.
28
29 [co]: /help?cmd=checkout
30 [cvsmu]: http://web.mit.edu/gnu/doc/html/cvs_7.html#SEC37
31 [up]: /help?cmd=update
32
--- www/co-vs-up.md
+++ www/co-vs-up.md
@@ -24,8 +24,8 @@
24
25 In summary, these are two separate commands; neither is an alias for the
26 other. They overlap enough that they can be used interchangeably for
27 some use cases, but `update` is more powerful and more broadly useful.
28
29 [co]: /help/checkout
30 [cvsmu]: http://web.mit.edu/gnu/doc/html/cvs_7.html#SEC37
31 [up]: /help/update
32
--- www/concepts.wiki
+++ www/concepts.wiki
@@ -435,24 +435,24 @@
435435
a lot of work and normally takes time, patience, and a lot of system
436436
knowledge. Fossil is designed to avoid this frustration. Setting up
437437
a server with Fossil is ridiculously easy. You have four options:
438438
439439
# <b>Stand-alone server.</b>
440
- Simply run the [/help?cmd=server|fossil server] or
441
- [/help?cmd=ui|fossil ui] command from the command-line.
440
+ Simply run the [/help/server|fossil server] or
441
+ [/help/ui|fossil ui] command from the command-line.
442442
<br><br>
443443
# <b>CGI.</b>
444444
Install a 2-line CGI script on a CGI-enabled web-server like Apache.
445445
<br><br>
446446
# <b>SCGI.</b>
447447
Start an SCGI server using the
448
- [/help?cmd=server| fossil server --scgi] command for handling
448
+ [/help/server| fossil server --scgi] command for handling
449449
SCGI requests from web-servers like Nginx.
450450
<br><br>
451451
# <b>Inetd or Stunnel.</b>
452452
Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests
453
- directly to the [/help?cmd=http|fossil http] command.
453
+ directly to the [/help/http|fossil http] command.
454454
455455
See the [./server/ | How To Configure A Fossil Server] document
456456
for details.
457457
458458
<h2>6.0 Review Of Key Concepts</h2>
459459
--- www/concepts.wiki
+++ www/concepts.wiki
@@ -435,24 +435,24 @@
435 a lot of work and normally takes time, patience, and a lot of system
436 knowledge. Fossil is designed to avoid this frustration. Setting up
437 a server with Fossil is ridiculously easy. You have four options:
438
439 # <b>Stand-alone server.</b>
440 Simply run the [/help?cmd=server|fossil server] or
441 [/help?cmd=ui|fossil ui] command from the command-line.
442 <br><br>
443 # <b>CGI.</b>
444 Install a 2-line CGI script on a CGI-enabled web-server like Apache.
445 <br><br>
446 # <b>SCGI.</b>
447 Start an SCGI server using the
448 [/help?cmd=server| fossil server --scgi] command for handling
449 SCGI requests from web-servers like Nginx.
450 <br><br>
451 # <b>Inetd or Stunnel.</b>
452 Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests
453 directly to the [/help?cmd=http|fossil http] command.
454
455 See the [./server/ | How To Configure A Fossil Server] document
456 for details.
457
458 <h2>6.0 Review Of Key Concepts</h2>
459
--- www/concepts.wiki
+++ www/concepts.wiki
@@ -435,24 +435,24 @@
435 a lot of work and normally takes time, patience, and a lot of system
436 knowledge. Fossil is designed to avoid this frustration. Setting up
437 a server with Fossil is ridiculously easy. You have four options:
438
439 # <b>Stand-alone server.</b>
440 Simply run the [/help/server|fossil server] or
441 [/help/ui|fossil ui] command from the command-line.
442 <br><br>
443 # <b>CGI.</b>
444 Install a 2-line CGI script on a CGI-enabled web-server like Apache.
445 <br><br>
446 # <b>SCGI.</b>
447 Start an SCGI server using the
448 [/help/server| fossil server --scgi] command for handling
449 SCGI requests from web-servers like Nginx.
450 <br><br>
451 # <b>Inetd or Stunnel.</b>
452 Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests
453 directly to the [/help/http|fossil http] command.
454
455 See the [./server/ | How To Configure A Fossil Server] document
456 for details.
457
458 <h2>6.0 Review Of Key Concepts</h2>
459
--- www/containers.md
+++ www/containers.md
@@ -541,12 +541,11 @@
541541
542542
I was then able to enable email alert forwarding for select repositories
543543
after configuring them per [the docs](./alerts.md) by saying:
544544
545545
$ systemctl --user daemon-reload
546
- $ systemctl --user enable alert-sender@myproject
547
- $ systemctl --user start alert-sender@myproject
546
+ $ systemctl --user enable --now alert-sender@myproject
548547
549548
Because this is a parameterized script and we’ve set our repository
550549
paths predictably, you can do this for as many repositories as you need
551550
to by passing their names after the “`@`” sign in the commands above.
552551
553552
--- www/containers.md
+++ www/containers.md
@@ -541,12 +541,11 @@
541
542 I was then able to enable email alert forwarding for select repositories
543 after configuring them per [the docs](./alerts.md) by saying:
544
545 $ systemctl --user daemon-reload
546 $ systemctl --user enable alert-sender@myproject
547 $ systemctl --user start alert-sender@myproject
548
549 Because this is a parameterized script and we’ve set our repository
550 paths predictably, you can do this for as many repositories as you need
551 to by passing their names after the “`@`” sign in the commands above.
552
553
--- www/containers.md
+++ www/containers.md
@@ -541,12 +541,11 @@
541
542 I was then able to enable email alert forwarding for select repositories
543 after configuring them per [the docs](./alerts.md) by saying:
544
545 $ systemctl --user daemon-reload
546 $ systemctl --user enable --now alert-sender@myproject
 
547
548 Because this is a parameterized script and we’ve set our repository
549 paths predictably, you can do this for as many repositories as you need
550 to by passing their names after the “`@`” sign in the commands above.
551
552
--- www/contribute.wiki
+++ www/contribute.wiki
@@ -40,11 +40,11 @@
4040
describe in detail what the patch does and which version of Fossil
4141
it is written against. It's best to make patches against tip-of-trunk
4242
rather than against past releases.
4343
4444
If your change is more complicated than a patch can properly encode, you
45
-may submit [/help?cmd=bundle | a Fossil bundle] instead. Unlike patches,
45
+may submit [/help/bundle | a Fossil bundle] instead. Unlike patches,
4646
bundles can contain multiple commits, check-in comments, file renames,
4747
file deletions, branching decisions, and more which <tt>patch(1)</tt>
4848
files cannot. It's best to make a bundle of a new branch so the change
4949
can be integrated, tested, enhanced, and merged down to trunk in a
5050
controlled fashion.
5151
--- www/contribute.wiki
+++ www/contribute.wiki
@@ -40,11 +40,11 @@
40 describe in detail what the patch does and which version of Fossil
41 it is written against. It's best to make patches against tip-of-trunk
42 rather than against past releases.
43
44 If your change is more complicated than a patch can properly encode, you
45 may submit [/help?cmd=bundle | a Fossil bundle] instead. Unlike patches,
46 bundles can contain multiple commits, check-in comments, file renames,
47 file deletions, branching decisions, and more which <tt>patch(1)</tt>
48 files cannot. It's best to make a bundle of a new branch so the change
49 can be integrated, tested, enhanced, and merged down to trunk in a
50 controlled fashion.
51
--- www/contribute.wiki
+++ www/contribute.wiki
@@ -40,11 +40,11 @@
40 describe in detail what the patch does and which version of Fossil
41 it is written against. It's best to make patches against tip-of-trunk
42 rather than against past releases.
43
44 If your change is more complicated than a patch can properly encode, you
45 may submit [/help/bundle | a Fossil bundle] instead. Unlike patches,
46 bundles can contain multiple commits, check-in comments, file renames,
47 file deletions, branching decisions, and more which <tt>patch(1)</tt>
48 files cannot. It's best to make a bundle of a new branch so the change
49 can be integrated, tested, enhanced, and merged down to trunk in a
50 controlled fashion.
51
--- www/customskin.md
+++ www/customskin.md
@@ -23,18 +23,18 @@
2323
* footer.txt
2424
* header.txt
2525
* js.txt
2626
2727
Try out the built-in skins by using the --skin option on the
28
-[fossil ui](/help?cmd=ui) or [fossil server](/help?cmd=server) commands.
28
+[fossil ui](/help/ui) or [fossil server](/help/server) commands.
2929
3030
## <a id="sharing"></a>Sharing Skins
3131
3232
The skin of a repository is not part of the versioned state and does not
3333
"push" or "pull" like checked-in files. The skin is local to the
3434
repository. However, skins can be shared between repositories using
35
-the [fossil config](/help?cmd=configuration) command.
35
+the [fossil config](/help/configuration) command.
3636
The "fossil config push skin" command will send the local skin to a remote
3737
repository and the "fossil config pull skin" command will import a skin
3838
from a remote repository. The "fossil config export skin FILENAME"
3939
will export the skin for a repository into a file FILENAME. This file
4040
can then be imported into a different repository using the
@@ -304,11 +304,11 @@
304304
305305
### Skin Development Using A Local Text Editor
306306
307307
An alternative approach is to copy the five control files for your
308308
baseline skin into a temporary working directory (here called
309
-"./newskin") and then launch the [fossil ui](/help?cmd=ui) command
309
+"./newskin") and then launch the [fossil ui](/help/ui) command
310310
with the "--skin ./newskin" option. If the argument to the --skin
311311
option contains a "/" character, then the five control files are
312312
read out of the directory named. You can then edit the control
313313
files in the ./newskin folder using you favorite text editor, and
314314
press "Reload" on your browser to see the effects.
@@ -515,11 +515,11 @@
515515
CSS, footer, and header editing screens under the Admin menu will
516516
work just as well. The important point is that the three files
517517
be named exactly "css.txt", "footer.txt", and "header.txt" and that
518518
they all be in the same directory.
519519
520
- 2. Run the [fossil ui](/help?cmd=ui) command with an extra
520
+ 2. Run the [fossil ui](/help/ui) command with an extra
521521
option "--skin SKINDIR" where SKINDIR is the name of the directory
522522
in which the three txt files were stored in step 1. This will bring
523523
up the Fossil website using the tree files in SKINDIR.
524524
525525
3. Edit the *.txt files in SKINDIR. After making each small change,
526526
--- www/customskin.md
+++ www/customskin.md
@@ -23,18 +23,18 @@
23 * footer.txt
24 * header.txt
25 * js.txt
26
27 Try out the built-in skins by using the --skin option on the
28 [fossil ui](/help?cmd=ui) or [fossil server](/help?cmd=server) commands.
29
30 ## <a id="sharing"></a>Sharing Skins
31
32 The skin of a repository is not part of the versioned state and does not
33 "push" or "pull" like checked-in files. The skin is local to the
34 repository. However, skins can be shared between repositories using
35 the [fossil config](/help?cmd=configuration) command.
36 The "fossil config push skin" command will send the local skin to a remote
37 repository and the "fossil config pull skin" command will import a skin
38 from a remote repository. The "fossil config export skin FILENAME"
39 will export the skin for a repository into a file FILENAME. This file
40 can then be imported into a different repository using the
@@ -304,11 +304,11 @@
304
305 ### Skin Development Using A Local Text Editor
306
307 An alternative approach is to copy the five control files for your
308 baseline skin into a temporary working directory (here called
309 "./newskin") and then launch the [fossil ui](/help?cmd=ui) command
310 with the "--skin ./newskin" option. If the argument to the --skin
311 option contains a "/" character, then the five control files are
312 read out of the directory named. You can then edit the control
313 files in the ./newskin folder using you favorite text editor, and
314 press "Reload" on your browser to see the effects.
@@ -515,11 +515,11 @@
515 CSS, footer, and header editing screens under the Admin menu will
516 work just as well. The important point is that the three files
517 be named exactly "css.txt", "footer.txt", and "header.txt" and that
518 they all be in the same directory.
519
520 2. Run the [fossil ui](/help?cmd=ui) command with an extra
521 option "--skin SKINDIR" where SKINDIR is the name of the directory
522 in which the three txt files were stored in step 1. This will bring
523 up the Fossil website using the tree files in SKINDIR.
524
525 3. Edit the *.txt files in SKINDIR. After making each small change,
526
--- www/customskin.md
+++ www/customskin.md
@@ -23,18 +23,18 @@
23 * footer.txt
24 * header.txt
25 * js.txt
26
27 Try out the built-in skins by using the --skin option on the
28 [fossil ui](/help/ui) or [fossil server](/help/server) commands.
29
30 ## <a id="sharing"></a>Sharing Skins
31
32 The skin of a repository is not part of the versioned state and does not
33 "push" or "pull" like checked-in files. The skin is local to the
34 repository. However, skins can be shared between repositories using
35 the [fossil config](/help/configuration) command.
36 The "fossil config push skin" command will send the local skin to a remote
37 repository and the "fossil config pull skin" command will import a skin
38 from a remote repository. The "fossil config export skin FILENAME"
39 will export the skin for a repository into a file FILENAME. This file
40 can then be imported into a different repository using the
@@ -304,11 +304,11 @@
304
305 ### Skin Development Using A Local Text Editor
306
307 An alternative approach is to copy the five control files for your
308 baseline skin into a temporary working directory (here called
309 "./newskin") and then launch the [fossil ui](/help/ui) command
310 with the "--skin ./newskin" option. If the argument to the --skin
311 option contains a "/" character, then the five control files are
312 read out of the directory named. You can then edit the control
313 files in the ./newskin folder using you favorite text editor, and
314 press "Reload" on your browser to see the effects.
@@ -515,11 +515,11 @@
515 CSS, footer, and header editing screens under the Admin menu will
516 work just as well. The important point is that the three files
517 be named exactly "css.txt", "footer.txt", and "header.txt" and that
518 they all be in the same directory.
519
520 2. Run the [fossil ui](/help/ui) command with an extra
521 option "--skin SKINDIR" where SKINDIR is the name of the directory
522 in which the three txt files were stored in step 1. This will bring
523 up the Fossil website using the tree files in SKINDIR.
524
525 3. Edit the *.txt files in SKINDIR. After making each small change,
526
+6 -6
--- www/defcsp.md
+++ www/defcsp.md
@@ -31,11 +31,11 @@
3131
img-src * data:;
3232
</pre>
3333
3434
The default is recommended for most installations. However,
3535
the site administrators can overwrite this default CSP using the
36
-[default-csp setting](/help?cmd=default-csp). For example,
36
+[default-csp setting](/help/default-csp). For example,
3737
CSP restrictions can be completely disabled by setting the default-csp to:
3838
3939
default-src *;
4040
4141
The following sections detail the maining of the default CSP setting.
@@ -286,25 +286,25 @@
286286
287287
Unversioned content is in the middle of the first list above — between
288288
fully-external content and fully in-repo content — because it isn’t
289289
included in a clone unless you give the `--unversioned` flag. If you
290290
then want updates to the unversioned content to be included in syncs,
291
-you have to give the same flag to [a `sync` command](/help?cmd=sync).
291
+you have to give the same flag to [a `sync` command](/help/sync).
292292
There is no equivalent with other commands such as `up` and `pull`, so
293293
you must then remember to give `fossil uv` commands when necessary to
294294
pull new unversioned content down.
295295
296296
Thus our recommendation that you refer to in-repo resources exclusively.
297297
298
-[du]: /help?cmd=/doc
298
+[du]: /help/www/doc
299299
[fp]: ./forum.wiki
300
-[ru]: /help?cmd=/raw
300
+[ru]: /help/www/raw
301301
[spof]: https://en.wikipedia.org/wiki/Single_point_of_failure
302302
[tkt]: ./tickets.wiki
303303
[tn]: ./event.wiki
304304
[tls]: ./server/debian/nginx.md
305
-[uu]: /help?cmd=/uv
305
+[uu]: /help/www/uv
306306
[uv]: ./unvers.wiki
307307
[wiki]: ./wikitheory.wiki
308308
309309
310310
## <a id="override"></a>Overriding the Default CSP
@@ -318,11 +318,11 @@
318318
a higher-level method.
319319
320320
321321
### <a id="cspsetting"></a>The `default-csp` Setting
322322
323
-If the [`default-csp` setting](/help?cmd=default-csp) is defined and is
323
+If the [`default-csp` setting](/help/default-csp) is defined and is
324324
not an empty string, its value is injected into the page using
325325
[TH1](./th1.md) via one or more of the methods below, depending on the
326326
skin you’re using and local configuration.
327327
328328
Changing this setting is the easiest way to set a nonstandard CSP on
329329
--- www/defcsp.md
+++ www/defcsp.md
@@ -31,11 +31,11 @@
31 img-src * data:;
32 </pre>
33
34 The default is recommended for most installations. However,
35 the site administrators can overwrite this default CSP using the
36 [default-csp setting](/help?cmd=default-csp). For example,
37 CSP restrictions can be completely disabled by setting the default-csp to:
38
39 default-src *;
40
41 The following sections detail the maining of the default CSP setting.
@@ -286,25 +286,25 @@
286
287 Unversioned content is in the middle of the first list above — between
288 fully-external content and fully in-repo content — because it isn’t
289 included in a clone unless you give the `--unversioned` flag. If you
290 then want updates to the unversioned content to be included in syncs,
291 you have to give the same flag to [a `sync` command](/help?cmd=sync).
292 There is no equivalent with other commands such as `up` and `pull`, so
293 you must then remember to give `fossil uv` commands when necessary to
294 pull new unversioned content down.
295
296 Thus our recommendation that you refer to in-repo resources exclusively.
297
298 [du]: /help?cmd=/doc
299 [fp]: ./forum.wiki
300 [ru]: /help?cmd=/raw
301 [spof]: https://en.wikipedia.org/wiki/Single_point_of_failure
302 [tkt]: ./tickets.wiki
303 [tn]: ./event.wiki
304 [tls]: ./server/debian/nginx.md
305 [uu]: /help?cmd=/uv
306 [uv]: ./unvers.wiki
307 [wiki]: ./wikitheory.wiki
308
309
310 ## <a id="override"></a>Overriding the Default CSP
@@ -318,11 +318,11 @@
318 a higher-level method.
319
320
321 ### <a id="cspsetting"></a>The `default-csp` Setting
322
323 If the [`default-csp` setting](/help?cmd=default-csp) is defined and is
324 not an empty string, its value is injected into the page using
325 [TH1](./th1.md) via one or more of the methods below, depending on the
326 skin you’re using and local configuration.
327
328 Changing this setting is the easiest way to set a nonstandard CSP on
329
--- www/defcsp.md
+++ www/defcsp.md
@@ -31,11 +31,11 @@
31 img-src * data:;
32 </pre>
33
34 The default is recommended for most installations. However,
35 the site administrators can overwrite this default CSP using the
36 [default-csp setting](/help/default-csp). For example,
37 CSP restrictions can be completely disabled by setting the default-csp to:
38
39 default-src *;
40
41 The following sections detail the maining of the default CSP setting.
@@ -286,25 +286,25 @@
286
287 Unversioned content is in the middle of the first list above — between
288 fully-external content and fully in-repo content — because it isn’t
289 included in a clone unless you give the `--unversioned` flag. If you
290 then want updates to the unversioned content to be included in syncs,
291 you have to give the same flag to [a `sync` command](/help/sync).
292 There is no equivalent with other commands such as `up` and `pull`, so
293 you must then remember to give `fossil uv` commands when necessary to
294 pull new unversioned content down.
295
296 Thus our recommendation that you refer to in-repo resources exclusively.
297
298 [du]: /help/www/doc
299 [fp]: ./forum.wiki
300 [ru]: /help/www/raw
301 [spof]: https://en.wikipedia.org/wiki/Single_point_of_failure
302 [tkt]: ./tickets.wiki
303 [tn]: ./event.wiki
304 [tls]: ./server/debian/nginx.md
305 [uu]: /help/www/uv
306 [uv]: ./unvers.wiki
307 [wiki]: ./wikitheory.wiki
308
309
310 ## <a id="override"></a>Overriding the Default CSP
@@ -318,11 +318,11 @@
318 a higher-level method.
319
320
321 ### <a id="cspsetting"></a>The `default-csp` Setting
322
323 If the [`default-csp` setting](/help/default-csp) is defined and is
324 not an empty string, its value is injected into the page using
325 [TH1](./th1.md) via one or more of the methods below, depending on the
326 skin you’re using and local configuration.
327
328 Changing this setting is the easiest way to set a nonstandard CSP on
329
--- www/delta_format.wiki
+++ www/delta_format.wiki
@@ -26,24 +26,24 @@
2626
[../src/deltafunc.c|deltafunc.c] source file.
2727
2828
The following command-line tools are available to create and apply
2929
deltas and to test the delta logic:
3030
31
- * [/help?cmd=test-delta|fossil test-delta] &rarr; Run self-tests of
31
+ * [/help/test-delta|fossil test-delta] &rarr; Run self-tests of
3232
the delta logic
3333
34
- * [/help?cmd=test-delta-create|fossil test-delta-create X Y] &rarr; compute
34
+ * [/help/test-delta-create|fossil test-delta-create X Y] &rarr; compute
3535
a delta that converts file X into file Y. Output that delta.
3636
37
- * [/help?cmd=test-delta-apply|fossil test-delta-apply X D] &rarr; apply
37
+ * [/help/test-delta-apply|fossil test-delta-apply X D] &rarr; apply
3838
delta D to input file X and output the result.
3939
40
- * [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] &rarr; compute
40
+ * [/help/test-delta-analyze|fossil test-delta-analyze X Y] &rarr; compute
4141
and delta that converts file X into file Y but instead of writing the
4242
delta to output, write performance information about the delta.
4343
44
-When running the [/help?cmd=sqlite3|fossil sql] command to get an
44
+When running the [/help/sqlite3|fossil sql] command to get an
4545
interactive SQL session connected to the repository, the following
4646
additional SQL functions are provided:
4747
4848
* <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> &rarr;
4949
Compute a data that carries blob X into blob Y and return that delta
5050
--- www/delta_format.wiki
+++ www/delta_format.wiki
@@ -26,24 +26,24 @@
26 [../src/deltafunc.c|deltafunc.c] source file.
27
28 The following command-line tools are available to create and apply
29 deltas and to test the delta logic:
30
31 * [/help?cmd=test-delta|fossil test-delta] &rarr; Run self-tests of
32 the delta logic
33
34 * [/help?cmd=test-delta-create|fossil test-delta-create X Y] &rarr; compute
35 a delta that converts file X into file Y. Output that delta.
36
37 * [/help?cmd=test-delta-apply|fossil test-delta-apply X D] &rarr; apply
38 delta D to input file X and output the result.
39
40 * [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] &rarr; compute
41 and delta that converts file X into file Y but instead of writing the
42 delta to output, write performance information about the delta.
43
44 When running the [/help?cmd=sqlite3|fossil sql] command to get an
45 interactive SQL session connected to the repository, the following
46 additional SQL functions are provided:
47
48 * <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> &rarr;
49 Compute a data that carries blob X into blob Y and return that delta
50
--- www/delta_format.wiki
+++ www/delta_format.wiki
@@ -26,24 +26,24 @@
26 [../src/deltafunc.c|deltafunc.c] source file.
27
28 The following command-line tools are available to create and apply
29 deltas and to test the delta logic:
30
31 * [/help/test-delta|fossil test-delta] &rarr; Run self-tests of
32 the delta logic
33
34 * [/help/test-delta-create|fossil test-delta-create X Y] &rarr; compute
35 a delta that converts file X into file Y. Output that delta.
36
37 * [/help/test-delta-apply|fossil test-delta-apply X D] &rarr; apply
38 delta D to input file X and output the result.
39
40 * [/help/test-delta-analyze|fossil test-delta-analyze X Y] &rarr; compute
41 and delta that converts file X into file Y but instead of writing the
42 delta to output, write performance information about the delta.
43
44 When running the [/help/sqlite3|fossil sql] command to get an
45 interactive SQL session connected to the repository, the following
46 additional SQL functions are provided:
47
48 * <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> &rarr;
49 Compute a data that carries blob X into blob Y and return that delta
50
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -36,11 +36,11 @@
3636
</pre>
3737
3838
The <i>&lt;baseurl&gt;</i> is the main URL used to access the fossil web server.
3939
For example, the <i>&lt;baseurl&gt;</i> for the fossil project itself is
4040
[https://fossil-scm.org/home].
41
-If you launch the web server using the "[/help?cmd=ui|fossil ui]" command line,
41
+If you launch the web server using the "[/help/ui|fossil ui]" command line,
4242
then the <i>&lt;baseurl&gt;</i> is usually
4343
<b>http://localhost:8080/</b>.
4444
4545
The <i>&lt;version&gt;</i> is the
4646
[./checkin_names.wiki|name of a check-in]
@@ -53,11 +53,11 @@
5353
also be the special identifier "<b>ckout</b>".
5454
The "<b>ckout</b>" keywords means to
5555
pull the documentation file from the local source tree on disk, not
5656
from the any check-in. The "<b>ckout</b>" keyword
5757
only works when you start your server using the
58
-"[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]"
58
+"[/help/server|fossil server]" or "[/help?cmd=ui|fossil ui]"
5959
commands. The "/doc/ckout" URL is intended to show a preview of
6060
the documentation you are currently editing but have not yet checked in.
6161
6262
The original designed purpose of the "ckout" feature is to allow the
6363
user to preview local changes to documentation before committing the
6464
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -36,11 +36,11 @@
36 </pre>
37
38 The <i>&lt;baseurl&gt;</i> is the main URL used to access the fossil web server.
39 For example, the <i>&lt;baseurl&gt;</i> for the fossil project itself is
40 [https://fossil-scm.org/home].
41 If you launch the web server using the "[/help?cmd=ui|fossil ui]" command line,
42 then the <i>&lt;baseurl&gt;</i> is usually
43 <b>http://localhost:8080/</b>.
44
45 The <i>&lt;version&gt;</i> is the
46 [./checkin_names.wiki|name of a check-in]
@@ -53,11 +53,11 @@
53 also be the special identifier "<b>ckout</b>".
54 The "<b>ckout</b>" keywords means to
55 pull the documentation file from the local source tree on disk, not
56 from the any check-in. The "<b>ckout</b>" keyword
57 only works when you start your server using the
58 "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]"
59 commands. The "/doc/ckout" URL is intended to show a preview of
60 the documentation you are currently editing but have not yet checked in.
61
62 The original designed purpose of the "ckout" feature is to allow the
63 user to preview local changes to documentation before committing the
64
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -36,11 +36,11 @@
36 </pre>
37
38 The <i>&lt;baseurl&gt;</i> is the main URL used to access the fossil web server.
39 For example, the <i>&lt;baseurl&gt;</i> for the fossil project itself is
40 [https://fossil-scm.org/home].
41 If you launch the web server using the "[/help/ui|fossil ui]" command line,
42 then the <i>&lt;baseurl&gt;</i> is usually
43 <b>http://localhost:8080/</b>.
44
45 The <i>&lt;version&gt;</i> is the
46 [./checkin_names.wiki|name of a check-in]
@@ -53,11 +53,11 @@
53 also be the special identifier "<b>ckout</b>".
54 The "<b>ckout</b>" keywords means to
55 pull the documentation file from the local source tree on disk, not
56 from the any check-in. The "<b>ckout</b>" keyword
57 only works when you start your server using the
58 "[/help/server|fossil server]" or "[/help?cmd=ui|fossil ui]"
59 commands. The "/doc/ckout" URL is intended to show a preview of
60 the documentation you are currently editing but have not yet checked in.
61
62 The original designed purpose of the "ckout" feature is to allow the
63 user to preview local changes to documentation before committing the
64
+1 -1
--- www/env-opts.md
+++ www/env-opts.md
@@ -253,11 +253,11 @@
253253
`SCRIPT_NAME`: If defined, included in error log messages.
254254
255255
`SSH_CONNECTION`: Informs CGI processing if the remote client is SSH.
256256
257257
`SSL_CERT_FILE`, `SSL_CERT_DIR`: Override the [`ssl-ca-location`]
258
-(/help?cmd=ssl-ca-location) setting.
258
+(/help/ssl-ca-location) setting.
259259
260260
`SQLITE_FORCE_PROXY_LOCKING`: From `sqlite3.c`, 1 means force always
261261
use proxy, 0 means never use proxy, and undefined means use proxy for
262262
non-local files only.
263263
264264
--- www/env-opts.md
+++ www/env-opts.md
@@ -253,11 +253,11 @@
253 `SCRIPT_NAME`: If defined, included in error log messages.
254
255 `SSH_CONNECTION`: Informs CGI processing if the remote client is SSH.
256
257 `SSL_CERT_FILE`, `SSL_CERT_DIR`: Override the [`ssl-ca-location`]
258 (/help?cmd=ssl-ca-location) setting.
259
260 `SQLITE_FORCE_PROXY_LOCKING`: From `sqlite3.c`, 1 means force always
261 use proxy, 0 means never use proxy, and undefined means use proxy for
262 non-local files only.
263
264
--- www/env-opts.md
+++ www/env-opts.md
@@ -253,11 +253,11 @@
253 `SCRIPT_NAME`: If defined, included in error log messages.
254
255 `SSH_CONNECTION`: Informs CGI processing if the remote client is SSH.
256
257 `SSL_CERT_FILE`, `SSL_CERT_DIR`: Override the [`ssl-ca-location`]
258 (/help/ssl-ca-location) setting.
259
260 `SQLITE_FORCE_PROXY_LOCKING`: From `sqlite3.c`, 1 means force always
261 use proxy, 0 means never use proxy, and undefined means use proxy for
262 non-local files only.
263
264
+1 -1
--- www/event.wiki
+++ www/event.wiki
@@ -107,11 +107,11 @@
107107
This note describes changes in the Fossil snapshot for ...
108108
</verbatim>
109109
110110
The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of
111111
three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>.
112
-See the [/help?cmd=wiki | wiki help] for specifics.
112
+See the [/help/wiki | wiki help] for specifics.
113113
114114
Users must have check-in privileges (permission "i") in order to
115115
create or edit technotes. In addition, users must have create-wiki
116116
privilege (permission "f") to create new technotes and edit-wiki
117117
privilege (permission "k") in order to edit existing technotes.
118118
--- www/event.wiki
+++ www/event.wiki
@@ -107,11 +107,11 @@
107 This note describes changes in the Fossil snapshot for ...
108 </verbatim>
109
110 The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of
111 three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>.
112 See the [/help?cmd=wiki | wiki help] for specifics.
113
114 Users must have check-in privileges (permission "i") in order to
115 create or edit technotes. In addition, users must have create-wiki
116 privilege (permission "f") to create new technotes and edit-wiki
117 privilege (permission "k") in order to edit existing technotes.
118
--- www/event.wiki
+++ www/event.wiki
@@ -107,11 +107,11 @@
107 This note describes changes in the Fossil snapshot for ...
108 </verbatim>
109
110 The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of
111 three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>.
112 See the [/help/wiki | wiki help] for specifics.
113
114 Users must have check-in privileges (permission "i") in order to
115 create or edit technotes. In addition, users must have create-wiki
116 privilege (permission "f") to create new technotes and edit-wiki
117 privilege (permission "k") in order to edit existing technotes.
118
--- www/fileedit-page.md
+++ www/fileedit-page.md
@@ -13,11 +13,11 @@
1313
1414
## <a id="cap"></a> `/fileedit` Does *Nothing* by Default.
1515
1616
In order to "activate" it, a user with [the "setup"
1717
permission](./caps/index.md) must set the
18
-[fileedit-glob](/help?cmd=fileedit-glob) repository setting to a
18
+[fileedit-glob](/help/fileedit-glob) repository setting to a
1919
comma- or newline-delimited list of globs representing a whitelist of
2020
files which may be edited online. Any user with commit access may then
2121
edit files matching one of those globs. Certain pages within the UI
2222
get an "edit" link added to them when the current user's permissions
2323
and the whitelist both permit editing of that file.
2424
--- www/fileedit-page.md
+++ www/fileedit-page.md
@@ -13,11 +13,11 @@
13
14 ## <a id="cap"></a> `/fileedit` Does *Nothing* by Default.
15
16 In order to "activate" it, a user with [the "setup"
17 permission](./caps/index.md) must set the
18 [fileedit-glob](/help?cmd=fileedit-glob) repository setting to a
19 comma- or newline-delimited list of globs representing a whitelist of
20 files which may be edited online. Any user with commit access may then
21 edit files matching one of those globs. Certain pages within the UI
22 get an "edit" link added to them when the current user's permissions
23 and the whitelist both permit editing of that file.
24
--- www/fileedit-page.md
+++ www/fileedit-page.md
@@ -13,11 +13,11 @@
13
14 ## <a id="cap"></a> `/fileedit` Does *Nothing* by Default.
15
16 In order to "activate" it, a user with [the "setup"
17 permission](./caps/index.md) must set the
18 [fileedit-glob](/help/fileedit-glob) repository setting to a
19 comma- or newline-delimited list of globs representing a whitelist of
20 files which may be edited online. Any user with commit access may then
21 edit files matching one of those globs. Certain pages within the UI
22 get an "edit" link added to them when the current user's permissions
23 and the whitelist both permit editing of that file.
24
+1 -1
--- www/forum.wiki
+++ www/forum.wiki
@@ -372,11 +372,11 @@
372372
<h2 id="close-post">Closing Forum Posts</h2>
373373
374374
As of version 2.23, the forum interface supports the notion of
375375
"closing" posts. By default, only users with the [./caps/index.md|'s'
376376
and 'a' capabilities] may close or re-open posts, or reply to closed
377
-posts. If the [/help?cmd=forum-close-policy|forum-close-policy
377
+posts. If the [/help/forum-close-policy|forum-close-policy
378378
configuration option] is enabled then users with
379379
[./caps/index.md|forum-moderator permissions] may also perform those
380380
actions.
381381
382382
Closing a post has the following implications:
383383
--- www/forum.wiki
+++ www/forum.wiki
@@ -372,11 +372,11 @@
372 <h2 id="close-post">Closing Forum Posts</h2>
373
374 As of version 2.23, the forum interface supports the notion of
375 "closing" posts. By default, only users with the [./caps/index.md|'s'
376 and 'a' capabilities] may close or re-open posts, or reply to closed
377 posts. If the [/help?cmd=forum-close-policy|forum-close-policy
378 configuration option] is enabled then users with
379 [./caps/index.md|forum-moderator permissions] may also perform those
380 actions.
381
382 Closing a post has the following implications:
383
--- www/forum.wiki
+++ www/forum.wiki
@@ -372,11 +372,11 @@
372 <h2 id="close-post">Closing Forum Posts</h2>
373
374 As of version 2.23, the forum interface supports the notion of
375 "closing" posts. By default, only users with the [./caps/index.md|'s'
376 and 'a' capabilities] may close or re-open posts, or reply to closed
377 posts. If the [/help/forum-close-policy|forum-close-policy
378 configuration option] is enabled then users with
379 [./caps/index.md|forum-moderator permissions] may also perform those
380 actions.
381
382 Closing a post has the following implications:
383
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -105,11 +105,11 @@
105105
[./bugtheory.wiki | ticketing &amp; bug tracking],
106106
[./embeddeddoc.wiki | embedded documentation],
107107
[./event.wiki | technical notes], a [./forum.wiki | web forum],
108108
and a [./chat.md | chat service],
109109
all within a single nicely-designed [./customskin.md|skinnable] web
110
-[/help?cmd=ui|UI],
110
+[/help/ui|UI],
111111
protected by [./caps/ | a fine-grained role-based
112112
access control system].
113113
These additional capabilities are available for Git as 3rd-party
114114
add-ons, but with Fossil they are integrated into
115115
the design, to the point that it approximates
@@ -130,12 +130,12 @@
130130
sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files
131131
that you forgot to commit prior to the end of your working day, across
132132
all repos.
133133
134134
Whenever Fossil is told to modify the local checkout in some destructive
135
-way ([/help?cmd=rm|fossil rm], [/help?cmd=update|fossil update],
136
-[/help?cmd=revert|fossil revert], etc.) Fossil remembers the prior state
135
+way ([/help/rm|fossil rm], [/help/update|fossil update],
136
+[/help/revert|fossil revert], etc.) Fossil remembers the prior state
137137
and is able to return the check-out directory to that state with a
138138
<tt>fossil undo</tt> command. While you cannot undo a commit in Fossil
139139
— [#history | on purpose!] — as long as the change remains confined to
140140
the local check-out directory only, Fossil makes undo
141141
[https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in
@@ -443,11 +443,11 @@
443443
* <b>No easy drive-by contributions:</b> Git
444444
[https://www.git-scm.com/docs/git-request-pull|pull requests] offer
445445
a low-friction path to accepting
446446
[https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
447447
contributions]. Fossil's closest equivalents are its unique
448
- [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement
448
+ [/help/bundle|bundle] and [/help/patch|patch] features, which require higher engagement
449449
than firing off a PR.⁷ This difference comes directly from the
450450
initial designed purpose for each tool: the SQLite project doesn't
451451
accept outside contributions from previously-unknown developers, but
452452
the Linux kernel does.
453453
@@ -456,11 +456,11 @@
456456
committed locally. [#history|There is no rebasing mechanism in
457457
Fossil, on purpose.]
458458
459459
* <b>Sync over push:</b> Explicit pushes are uncommon in
460460
Fossil-based projects: the default is to rely on
461
- [/help?cmd=autosync|autosync mode] instead, in which each commit
461
+ [/help/autosync|autosync mode] instead, in which each commit
462462
syncs immediately to its parent repository. This is a mode so you
463463
can turn it off temporarily when needed, such as when working
464464
offline. Fossil is still a truly distributed version control system;
465465
it's just that its starting default is to assume you're rarely out
466466
of communication with the parent repo.
@@ -598,11 +598,11 @@
598598
that repository, the default mode of operation in Git is to stick to that
599599
single work/repo tree, even when that's a shortsighted way of working.
600600
601601
Fossil doesn't work that way. A Fossil repository is an SQLite database
602602
file which is normally stored outside the working checkout directory. You can
603
-[/help?cmd=open | open] a Fossil repository any number of times into
603
+[/help/open | open] a Fossil repository any number of times into
604604
any number of working directories. A common usage pattern is to have one
605605
working directory per active working branch, so that switching branches
606606
is done with a <tt>cd</tt> command rather than by checking out the
607607
branches successively in a single working directory.
608608
@@ -682,11 +682,11 @@
682682
makes the history of a Fossil project "messy," but another point of view
683683
is that this makes the history "accurate." In actual practice, the
684684
superior reporting tools available in Fossil mean that this incidental mess
685685
is not a factor.
686686
687
-Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying
687
+Like Git, Fossil has an [/help/amend|amend command] for modifying
688688
prior commits, but unlike in Git, this works not by replacing data in
689689
the repository, but by adding a correction record to the repository that
690690
affects how later Fossil operations present the corrected data. The old
691691
information is still there in the repository, it is just overridden from
692692
the amendment point forward.
@@ -712,11 +712,11 @@
712712
the receiving repo as well. Fossil's shun feature isn't for fixing up
713713
everyday bad commits, it's for dealing with extreme situations: public
714714
commits of secret material, ticket/wiki/forum spam, law enforcement
715715
takedown demands, etc.
716716
717
-There is also the experimental [/help?cmd=purge | <tt>purge</tt>
717
+There is also the experimental [/help/purge | <tt>purge</tt>
718718
command], which differs from shunning in ways that aren't especially
719719
important in the context of this document. At a 30000 foot level, you
720720
can think of purging as useful only when you've turned off Fossil's
721721
autosync feature and want to pluck artifacts out of its hash tree before
722722
they get pushed. In that sense, it's approximately the same as
@@ -821,11 +821,11 @@
821821
that Fossil's command interface is simpler than Git's: there are fewer
822822
concepts to keep track of in your mental model of Fossil's internal
823823
operation.
824824
825825
Fossil's implementation of the feature is also simpler to describe. The
826
-brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is
826
+brief online help for <tt>[/help/merge | fossil merge]</tt> is
827827
currently 41 lines long, to which you want to add the 600 lines of
828828
[./branching.wiki | the branching document]. The equivalent
829829
documentation in Git is the aggregation of the man pages for the above
830830
three commands, which is over 1000 lines, much of it mutually redundant.
831831
(e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get
832832
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -105,11 +105,11 @@
105 [./bugtheory.wiki | ticketing &amp; bug tracking],
106 [./embeddeddoc.wiki | embedded documentation],
107 [./event.wiki | technical notes], a [./forum.wiki | web forum],
108 and a [./chat.md | chat service],
109 all within a single nicely-designed [./customskin.md|skinnable] web
110 [/help?cmd=ui|UI],
111 protected by [./caps/ | a fine-grained role-based
112 access control system].
113 These additional capabilities are available for Git as 3rd-party
114 add-ons, but with Fossil they are integrated into
115 the design, to the point that it approximates
@@ -130,12 +130,12 @@
130 sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files
131 that you forgot to commit prior to the end of your working day, across
132 all repos.
133
134 Whenever Fossil is told to modify the local checkout in some destructive
135 way ([/help?cmd=rm|fossil rm], [/help?cmd=update|fossil update],
136 [/help?cmd=revert|fossil revert], etc.) Fossil remembers the prior state
137 and is able to return the check-out directory to that state with a
138 <tt>fossil undo</tt> command. While you cannot undo a commit in Fossil
139 — [#history | on purpose!] — as long as the change remains confined to
140 the local check-out directory only, Fossil makes undo
141 [https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in
@@ -443,11 +443,11 @@
443 * <b>No easy drive-by contributions:</b> Git
444 [https://www.git-scm.com/docs/git-request-pull|pull requests] offer
445 a low-friction path to accepting
446 [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
447 contributions]. Fossil's closest equivalents are its unique
448 [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement
449 than firing off a PR.⁷ This difference comes directly from the
450 initial designed purpose for each tool: the SQLite project doesn't
451 accept outside contributions from previously-unknown developers, but
452 the Linux kernel does.
453
@@ -456,11 +456,11 @@
456 committed locally. [#history|There is no rebasing mechanism in
457 Fossil, on purpose.]
458
459 * <b>Sync over push:</b> Explicit pushes are uncommon in
460 Fossil-based projects: the default is to rely on
461 [/help?cmd=autosync|autosync mode] instead, in which each commit
462 syncs immediately to its parent repository. This is a mode so you
463 can turn it off temporarily when needed, such as when working
464 offline. Fossil is still a truly distributed version control system;
465 it's just that its starting default is to assume you're rarely out
466 of communication with the parent repo.
@@ -598,11 +598,11 @@
598 that repository, the default mode of operation in Git is to stick to that
599 single work/repo tree, even when that's a shortsighted way of working.
600
601 Fossil doesn't work that way. A Fossil repository is an SQLite database
602 file which is normally stored outside the working checkout directory. You can
603 [/help?cmd=open | open] a Fossil repository any number of times into
604 any number of working directories. A common usage pattern is to have one
605 working directory per active working branch, so that switching branches
606 is done with a <tt>cd</tt> command rather than by checking out the
607 branches successively in a single working directory.
608
@@ -682,11 +682,11 @@
682 makes the history of a Fossil project "messy," but another point of view
683 is that this makes the history "accurate." In actual practice, the
684 superior reporting tools available in Fossil mean that this incidental mess
685 is not a factor.
686
687 Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying
688 prior commits, but unlike in Git, this works not by replacing data in
689 the repository, but by adding a correction record to the repository that
690 affects how later Fossil operations present the corrected data. The old
691 information is still there in the repository, it is just overridden from
692 the amendment point forward.
@@ -712,11 +712,11 @@
712 the receiving repo as well. Fossil's shun feature isn't for fixing up
713 everyday bad commits, it's for dealing with extreme situations: public
714 commits of secret material, ticket/wiki/forum spam, law enforcement
715 takedown demands, etc.
716
717 There is also the experimental [/help?cmd=purge | <tt>purge</tt>
718 command], which differs from shunning in ways that aren't especially
719 important in the context of this document. At a 30000 foot level, you
720 can think of purging as useful only when you've turned off Fossil's
721 autosync feature and want to pluck artifacts out of its hash tree before
722 they get pushed. In that sense, it's approximately the same as
@@ -821,11 +821,11 @@
821 that Fossil's command interface is simpler than Git's: there are fewer
822 concepts to keep track of in your mental model of Fossil's internal
823 operation.
824
825 Fossil's implementation of the feature is also simpler to describe. The
826 brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is
827 currently 41 lines long, to which you want to add the 600 lines of
828 [./branching.wiki | the branching document]. The equivalent
829 documentation in Git is the aggregation of the man pages for the above
830 three commands, which is over 1000 lines, much of it mutually redundant.
831 (e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get
832
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -105,11 +105,11 @@
105 [./bugtheory.wiki | ticketing &amp; bug tracking],
106 [./embeddeddoc.wiki | embedded documentation],
107 [./event.wiki | technical notes], a [./forum.wiki | web forum],
108 and a [./chat.md | chat service],
109 all within a single nicely-designed [./customskin.md|skinnable] web
110 [/help/ui|UI],
111 protected by [./caps/ | a fine-grained role-based
112 access control system].
113 These additional capabilities are available for Git as 3rd-party
114 add-ons, but with Fossil they are integrated into
115 the design, to the point that it approximates
@@ -130,12 +130,12 @@
130 sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files
131 that you forgot to commit prior to the end of your working day, across
132 all repos.
133
134 Whenever Fossil is told to modify the local checkout in some destructive
135 way ([/help/rm|fossil rm], [/help/update|fossil update],
136 [/help/revert|fossil revert], etc.) Fossil remembers the prior state
137 and is able to return the check-out directory to that state with a
138 <tt>fossil undo</tt> command. While you cannot undo a commit in Fossil
139 — [#history | on purpose!] — as long as the change remains confined to
140 the local check-out directory only, Fossil makes undo
141 [https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in
@@ -443,11 +443,11 @@
443 * <b>No easy drive-by contributions:</b> Git
444 [https://www.git-scm.com/docs/git-request-pull|pull requests] offer
445 a low-friction path to accepting
446 [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
447 contributions]. Fossil's closest equivalents are its unique
448 [/help/bundle|bundle] and [/help/patch|patch] features, which require higher engagement
449 than firing off a PR.⁷ This difference comes directly from the
450 initial designed purpose for each tool: the SQLite project doesn't
451 accept outside contributions from previously-unknown developers, but
452 the Linux kernel does.
453
@@ -456,11 +456,11 @@
456 committed locally. [#history|There is no rebasing mechanism in
457 Fossil, on purpose.]
458
459 * <b>Sync over push:</b> Explicit pushes are uncommon in
460 Fossil-based projects: the default is to rely on
461 [/help/autosync|autosync mode] instead, in which each commit
462 syncs immediately to its parent repository. This is a mode so you
463 can turn it off temporarily when needed, such as when working
464 offline. Fossil is still a truly distributed version control system;
465 it's just that its starting default is to assume you're rarely out
466 of communication with the parent repo.
@@ -598,11 +598,11 @@
598 that repository, the default mode of operation in Git is to stick to that
599 single work/repo tree, even when that's a shortsighted way of working.
600
601 Fossil doesn't work that way. A Fossil repository is an SQLite database
602 file which is normally stored outside the working checkout directory. You can
603 [/help/open | open] a Fossil repository any number of times into
604 any number of working directories. A common usage pattern is to have one
605 working directory per active working branch, so that switching branches
606 is done with a <tt>cd</tt> command rather than by checking out the
607 branches successively in a single working directory.
608
@@ -682,11 +682,11 @@
682 makes the history of a Fossil project "messy," but another point of view
683 is that this makes the history "accurate." In actual practice, the
684 superior reporting tools available in Fossil mean that this incidental mess
685 is not a factor.
686
687 Like Git, Fossil has an [/help/amend|amend command] for modifying
688 prior commits, but unlike in Git, this works not by replacing data in
689 the repository, but by adding a correction record to the repository that
690 affects how later Fossil operations present the corrected data. The old
691 information is still there in the repository, it is just overridden from
692 the amendment point forward.
@@ -712,11 +712,11 @@
712 the receiving repo as well. Fossil's shun feature isn't for fixing up
713 everyday bad commits, it's for dealing with extreme situations: public
714 commits of secret material, ticket/wiki/forum spam, law enforcement
715 takedown demands, etc.
716
717 There is also the experimental [/help/purge | <tt>purge</tt>
718 command], which differs from shunning in ways that aren't especially
719 important in the context of this document. At a 30000 foot level, you
720 can think of purging as useful only when you've turned off Fossil's
721 autosync feature and want to pluck artifacts out of its hash tree before
722 they get pushed. In that sense, it's approximately the same as
@@ -821,11 +821,11 @@
821 that Fossil's command interface is simpler than Git's: there are fewer
822 concepts to keep track of in your mental model of Fossil's internal
823 operation.
824
825 Fossil's implementation of the feature is also simpler to describe. The
826 brief online help for <tt>[/help/merge | fossil merge]</tt> is
827 currently 41 lines long, to which you want to add the 600 lines of
828 [./branching.wiki | the branching document]. The equivalent
829 documentation in Git is the aggregation of the man pages for the above
830 three commands, which is over 1000 lines, much of it mutually redundant.
831 (e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get
832
+22 -22
--- www/gitusers.md
+++ www/gitusers.md
@@ -37,11 +37,11 @@
3737
While we do try to explain Fossil-specific terminology inline here
3838
as-needed, you may find it helpful to skim [the Fossil glossary][gloss].
3939
It will give you another take on our definitions here, and it may help
4040
you to understand some of the other Fossil docs better.
4141
42
-[fbis]: /help?cmd=bisect
42
+[fbis]: /help/bisect
4343
[gbis]: https://git-scm.com/docs/git-bisect
4444
[ffor]: https://fossil-scm.org/forum
4545
[fvg]: ./fossil-v-git.wiki
4646
4747
@@ -90,11 +90,11 @@
9090
from the remote repository into the local clone as `fossil update` does.
9191
We think this is less broadly useful, but that’s the subject of the next
9292
section.
9393
9494
[ckwf]: ./ckout-workflows.md
95
-[co]: /help?cmd=checkout
95
+[co]: /help/checkout
9696
9797
9898
#### <a id="pullup"></a> Update vs Pull
9999
100100
The closest equivalent to [`git pull`][gpull] is not
@@ -131,11 +131,11 @@
131131
tip of the current branch.
132132
133133
We think this is a more sensible command design than `git pull` vs
134134
`git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan])
135135
136
-[fpull]: /help?cmd=pull
136
+[fpull]: /help/pull
137137
[gpull]: https://git-scm.com/docs/git-pull
138138
[gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well
139139
140140
141141
#### <a id="close" name="dotfile"></a> Closing a Check-Out
@@ -169,11 +169,11 @@
169169
is that you’re about to delete the directory, so you want Fossil to forget about it
170170
for the purposes of commands like [`fossil all`][all]. Even that isn’t
171171
necessary, because Fossil will detect that this has happened and forget
172172
the working directory for you.
173173
174
-[all]: /help?cmd=all
174
+[all]: /help/all
175175
176176
177177
#### <a id="worktree"></a> Git Worktrees
178178
179179
There are at least three different ways to get [Fossil-style multiple
@@ -271,18 +271,18 @@
271271
you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it
272272
does emphasize an earlier point: Fossil doesn’t care where you put the
273273
repo DB file or what you name it.
274274
275275
276
-[clone]: /help?cmd=clone
277
-[close]: /help?cmd=close
276
+[clone]: /help/clone
277
+[close]: /help/close
278278
[gloss]: ./glossary.md
279
-[open]: /help?cmd=open
280
-[set]: /help?cmd=setting
281
-[server]: /help?cmd=server
282
-[stash]: /help?cmd=stash
283
-[undo]: /help?cmd=undo
279
+[open]: /help/open
280
+[set]: /help/setting
281
+[server]: /help/server
282
+[stash]: /help/stash
283
+[undo]: /help/undo
284284
285285
286286
## <a id="log"></a> Fossil’s Timeline Is the “Log”
287287
288288
Git users often need to use the `git log` command to dig linearly through
@@ -416,16 +416,16 @@
416416
intermediate like “`f time desc curr`”, which is reasonably clear.
417417
418418
[35pct]: https://www.sqlite.org/fasterthanfs.html
419419
[btree]: https://sqlite.org/btreemodule.html
420420
[gcn]: https://git-scm.com/docs/gitrevisions
421
-[infoc]: /help?cmd=info
422
-[infow]: /help?cmd=/info
421
+[infoc]: /help/info
422
+[infow]: /help/www/info
423423
[ocomp]: https://www.bigocheatsheet.com/
424
-[tlc]: /help?cmd=timeline
425
-[tlw]: /help?cmd=/timeline
426
-[up]: /help?cmd=update
424
+[tlc]: /help/timeline
425
+[tlw]: /help/www/timeline
426
+[up]: /help/update
427427
[wdm]: ./fossil-v-git.wiki#durable
428428
429429
430430
## <a id="dhead"></a> Detached HEAD State
431431
@@ -631,11 +631,11 @@
631631
keeps using the same remote server URL you gave it last
632632
until you [tell it to do something different][rem]. It pushes all
633633
branches, not just one named local branch.
634634
635635
[capt]: ./cap-theorem.md
636
-[rem]: /help?cmd=remote
636
+[rem]: /help/remote
637637
638638
639639
<a id="autosync"></a>
640640
## Autosync
641641
@@ -941,11 +941,11 @@
941941
command set.
942942
943943
If you leave off the `-v` flag in the second example, the `diffstat`
944944
output won’t include info about any newly-added files.
945945
946
-[dcset]: https://fossil-scm.org/home/help?cmd=diff-command
946
+[dcset]: https://fossil-scm.org/home/help/diff-command
947947
[dst]: https://invisible-island.net/diffstat/diffstat.html
948948
949949
950950
<a id="btnames"></a>
951951
## Branch and Tag Names
@@ -964,11 +964,11 @@
964964
[The `fossil git export` command][fge] squashes repeated tags down to a
965965
single instance to avoid confusing Git, exporting only the newest tag,
966966
emulating Fossil’s own ambiguity resolution rule as best it can within
967967
Git’s limitations.
968968
969
-[fge]: /help?cmd=git
969
+[fge]: /help/git
970970
[gcrf]: https://git-scm.com/docs/git-check-ref-format
971971
972972
973973
974974
@@ -981,11 +981,11 @@
981981
both merge operations, and the two actions differ only in direction.
982982
983983
Unlike in Git, the Fossil file format remembers cherrypicks and backouts
984984
and can later show them as dashed lines on the graphical timeline.
985985
986
-[merge]: /help?cmd=merge
986
+[merge]: /help/merge
987987
988988
989989
990990
<a id="mvrm"></a>
991991
## File Moves and Renames Are Soft by Default
@@ -1005,12 +1005,12 @@
10051005
If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you
10061006
can cast it away on a per-command basis:
10071007
10081008
fossil mv --hard old-name new-name
10091009
1010
-[mv]: /help?cmd=mv
1011
-[rm]: /help?cmd=rm
1010
+[mv]: /help/mv
1011
+[rm]: /help/rm
10121012
10131013
10141014
----
10151015
10161016
10171017
--- www/gitusers.md
+++ www/gitusers.md
@@ -37,11 +37,11 @@
37 While we do try to explain Fossil-specific terminology inline here
38 as-needed, you may find it helpful to skim [the Fossil glossary][gloss].
39 It will give you another take on our definitions here, and it may help
40 you to understand some of the other Fossil docs better.
41
42 [fbis]: /help?cmd=bisect
43 [gbis]: https://git-scm.com/docs/git-bisect
44 [ffor]: https://fossil-scm.org/forum
45 [fvg]: ./fossil-v-git.wiki
46
47
@@ -90,11 +90,11 @@
90 from the remote repository into the local clone as `fossil update` does.
91 We think this is less broadly useful, but that’s the subject of the next
92 section.
93
94 [ckwf]: ./ckout-workflows.md
95 [co]: /help?cmd=checkout
96
97
98 #### <a id="pullup"></a> Update vs Pull
99
100 The closest equivalent to [`git pull`][gpull] is not
@@ -131,11 +131,11 @@
131 tip of the current branch.
132
133 We think this is a more sensible command design than `git pull` vs
134 `git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan])
135
136 [fpull]: /help?cmd=pull
137 [gpull]: https://git-scm.com/docs/git-pull
138 [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well
139
140
141 #### <a id="close" name="dotfile"></a> Closing a Check-Out
@@ -169,11 +169,11 @@
169 is that you’re about to delete the directory, so you want Fossil to forget about it
170 for the purposes of commands like [`fossil all`][all]. Even that isn’t
171 necessary, because Fossil will detect that this has happened and forget
172 the working directory for you.
173
174 [all]: /help?cmd=all
175
176
177 #### <a id="worktree"></a> Git Worktrees
178
179 There are at least three different ways to get [Fossil-style multiple
@@ -271,18 +271,18 @@
271 you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it
272 does emphasize an earlier point: Fossil doesn’t care where you put the
273 repo DB file or what you name it.
274
275
276 [clone]: /help?cmd=clone
277 [close]: /help?cmd=close
278 [gloss]: ./glossary.md
279 [open]: /help?cmd=open
280 [set]: /help?cmd=setting
281 [server]: /help?cmd=server
282 [stash]: /help?cmd=stash
283 [undo]: /help?cmd=undo
284
285
286 ## <a id="log"></a> Fossil’s Timeline Is the “Log”
287
288 Git users often need to use the `git log` command to dig linearly through
@@ -416,16 +416,16 @@
416 intermediate like “`f time desc curr`”, which is reasonably clear.
417
418 [35pct]: https://www.sqlite.org/fasterthanfs.html
419 [btree]: https://sqlite.org/btreemodule.html
420 [gcn]: https://git-scm.com/docs/gitrevisions
421 [infoc]: /help?cmd=info
422 [infow]: /help?cmd=/info
423 [ocomp]: https://www.bigocheatsheet.com/
424 [tlc]: /help?cmd=timeline
425 [tlw]: /help?cmd=/timeline
426 [up]: /help?cmd=update
427 [wdm]: ./fossil-v-git.wiki#durable
428
429
430 ## <a id="dhead"></a> Detached HEAD State
431
@@ -631,11 +631,11 @@
631 keeps using the same remote server URL you gave it last
632 until you [tell it to do something different][rem]. It pushes all
633 branches, not just one named local branch.
634
635 [capt]: ./cap-theorem.md
636 [rem]: /help?cmd=remote
637
638
639 <a id="autosync"></a>
640 ## Autosync
641
@@ -941,11 +941,11 @@
941 command set.
942
943 If you leave off the `-v` flag in the second example, the `diffstat`
944 output won’t include info about any newly-added files.
945
946 [dcset]: https://fossil-scm.org/home/help?cmd=diff-command
947 [dst]: https://invisible-island.net/diffstat/diffstat.html
948
949
950 <a id="btnames"></a>
951 ## Branch and Tag Names
@@ -964,11 +964,11 @@
964 [The `fossil git export` command][fge] squashes repeated tags down to a
965 single instance to avoid confusing Git, exporting only the newest tag,
966 emulating Fossil’s own ambiguity resolution rule as best it can within
967 Git’s limitations.
968
969 [fge]: /help?cmd=git
970 [gcrf]: https://git-scm.com/docs/git-check-ref-format
971
972
973
974
@@ -981,11 +981,11 @@
981 both merge operations, and the two actions differ only in direction.
982
983 Unlike in Git, the Fossil file format remembers cherrypicks and backouts
984 and can later show them as dashed lines on the graphical timeline.
985
986 [merge]: /help?cmd=merge
987
988
989
990 <a id="mvrm"></a>
991 ## File Moves and Renames Are Soft by Default
@@ -1005,12 +1005,12 @@
1005 If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you
1006 can cast it away on a per-command basis:
1007
1008 fossil mv --hard old-name new-name
1009
1010 [mv]: /help?cmd=mv
1011 [rm]: /help?cmd=rm
1012
1013
1014 ----
1015
1016
1017
--- www/gitusers.md
+++ www/gitusers.md
@@ -37,11 +37,11 @@
37 While we do try to explain Fossil-specific terminology inline here
38 as-needed, you may find it helpful to skim [the Fossil glossary][gloss].
39 It will give you another take on our definitions here, and it may help
40 you to understand some of the other Fossil docs better.
41
42 [fbis]: /help/bisect
43 [gbis]: https://git-scm.com/docs/git-bisect
44 [ffor]: https://fossil-scm.org/forum
45 [fvg]: ./fossil-v-git.wiki
46
47
@@ -90,11 +90,11 @@
90 from the remote repository into the local clone as `fossil update` does.
91 We think this is less broadly useful, but that’s the subject of the next
92 section.
93
94 [ckwf]: ./ckout-workflows.md
95 [co]: /help/checkout
96
97
98 #### <a id="pullup"></a> Update vs Pull
99
100 The closest equivalent to [`git pull`][gpull] is not
@@ -131,11 +131,11 @@
131 tip of the current branch.
132
133 We think this is a more sensible command design than `git pull` vs
134 `git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan])
135
136 [fpull]: /help/pull
137 [gpull]: https://git-scm.com/docs/git-pull
138 [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well
139
140
141 #### <a id="close" name="dotfile"></a> Closing a Check-Out
@@ -169,11 +169,11 @@
169 is that you’re about to delete the directory, so you want Fossil to forget about it
170 for the purposes of commands like [`fossil all`][all]. Even that isn’t
171 necessary, because Fossil will detect that this has happened and forget
172 the working directory for you.
173
174 [all]: /help/all
175
176
177 #### <a id="worktree"></a> Git Worktrees
178
179 There are at least three different ways to get [Fossil-style multiple
@@ -271,18 +271,18 @@
271 you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it
272 does emphasize an earlier point: Fossil doesn’t care where you put the
273 repo DB file or what you name it.
274
275
276 [clone]: /help/clone
277 [close]: /help/close
278 [gloss]: ./glossary.md
279 [open]: /help/open
280 [set]: /help/setting
281 [server]: /help/server
282 [stash]: /help/stash
283 [undo]: /help/undo
284
285
286 ## <a id="log"></a> Fossil’s Timeline Is the “Log”
287
288 Git users often need to use the `git log` command to dig linearly through
@@ -416,16 +416,16 @@
416 intermediate like “`f time desc curr`”, which is reasonably clear.
417
418 [35pct]: https://www.sqlite.org/fasterthanfs.html
419 [btree]: https://sqlite.org/btreemodule.html
420 [gcn]: https://git-scm.com/docs/gitrevisions
421 [infoc]: /help/info
422 [infow]: /help/www/info
423 [ocomp]: https://www.bigocheatsheet.com/
424 [tlc]: /help/timeline
425 [tlw]: /help/www/timeline
426 [up]: /help/update
427 [wdm]: ./fossil-v-git.wiki#durable
428
429
430 ## <a id="dhead"></a> Detached HEAD State
431
@@ -631,11 +631,11 @@
631 keeps using the same remote server URL you gave it last
632 until you [tell it to do something different][rem]. It pushes all
633 branches, not just one named local branch.
634
635 [capt]: ./cap-theorem.md
636 [rem]: /help/remote
637
638
639 <a id="autosync"></a>
640 ## Autosync
641
@@ -941,11 +941,11 @@
941 command set.
942
943 If you leave off the `-v` flag in the second example, the `diffstat`
944 output won’t include info about any newly-added files.
945
946 [dcset]: https://fossil-scm.org/home/help/diff-command
947 [dst]: https://invisible-island.net/diffstat/diffstat.html
948
949
950 <a id="btnames"></a>
951 ## Branch and Tag Names
@@ -964,11 +964,11 @@
964 [The `fossil git export` command][fge] squashes repeated tags down to a
965 single instance to avoid confusing Git, exporting only the newest tag,
966 emulating Fossil’s own ambiguity resolution rule as best it can within
967 Git’s limitations.
968
969 [fge]: /help/git
970 [gcrf]: https://git-scm.com/docs/git-check-ref-format
971
972
973
974
@@ -981,11 +981,11 @@
981 both merge operations, and the two actions differ only in direction.
982
983 Unlike in Git, the Fossil file format remembers cherrypicks and backouts
984 and can later show them as dashed lines on the graphical timeline.
985
986 [merge]: /help/merge
987
988
989
990 <a id="mvrm"></a>
991 ## File Moves and Renames Are Soft by Default
@@ -1005,12 +1005,12 @@
1005 If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you
1006 can cast it away on a per-command basis:
1007
1008 fossil mv --hard old-name new-name
1009
1010 [mv]: /help/mv
1011 [rm]: /help/rm
1012
1013
1014 ----
1015
1016
1017
+24 -24
--- www/globs.md
+++ www/globs.md
@@ -242,29 +242,29 @@
242242
The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that
243243
implement or support with web servers provide a mechanism to name some
244244
files to serve with static content where a list of glob patterns
245245
specifies what content may be served.
246246
247
-[`add`]: /help?cmd=add
248
-[`addremove`]: /help?cmd=addremove
249
-[`changes`]: /help?cmd=changes
250
-[`clean`]: /help?cmd=clean
251
-[`commit`]: /help?cmd=commit
252
-[`extras`]: /help?cmd=extras
253
-[`merge`]: /help?cmd=merge
254
-[`settings`]: /help?cmd=settings
255
-[`status`]: /help?cmd=status
256
-[`touch`]: /help?cmd=touch
257
-[`unset`]: /help?cmd=unset
258
-
259
-[`tarball`]: /help?cmd=tarball
260
-[`zip`]: /help?cmd=zip
261
-
262
-[`http`]: /help?cmd=http
263
-[`cgi`]: /help?cmd=cgi
264
-[`server`]: /help?cmd=server
265
-[`ui`]: /help?cmd=ui
247
+[`add`]: /help/add
248
+[`addremove`]: /help/addremove
249
+[`changes`]: /help/changes
250
+[`clean`]: /help/clean
251
+[`commit`]: /help/commit
252
+[`extras`]: /help/extras
253
+[`merge`]: /help/merge
254
+[`settings`]: /help/settings
255
+[`status`]: /help/status
256
+[`touch`]: /help/touch
257
+[`unset`]: /help/unset
258
+
259
+[`tarball`]: /help/tarball
260
+[`zip`]: /help/zip
261
+
262
+[`http`]: /help/http
263
+[`cgi`]: /help/cgi
264
+[`server`]: /help/server
265
+[`ui`]: /help/ui
266266
267267
268268
### Web Pages that Refer to Globs
269269
270270
The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that
@@ -277,13 +277,13 @@
277277
The pages [`/tarball`][] and [`/zip`][] generate compressed archives
278278
of a specific checkin. They may be further restricted by query
279279
parameters that specify glob patterns that name files to include or
280280
exclude rather than taking the entire checkin.
281281
282
-[`/timeline`]: /help?cmd=/timeline
283
-[`/tarball`]: /help?cmd=/tarball
284
-[`/zip`]: /help?cmd=/zip
282
+[`/timeline`]: /help/www/timeline
283
+[`/tarball`]: /help/www/tarball
284
+[`/zip`]: /help/www/zip
285285
286286
287287
## Platform Quirks
288288
289289
Fossil glob patterns are based on the glob pattern feature of POSIX
@@ -512,12 +512,12 @@
512512
C:\> echo * | fossil test-echo setting crlf-glob --args -
513513
514514
The [`test-glob`][] command is also handy to test if a string
515515
matches a glob pattern.
516516
517
-[`test-echo`]: /help?cmd=test-echo
518
-[`test-glob`]: /help?cmd=test-glob
517
+[`test-echo`]: /help/test-echo
518
+[`test-glob`]: /help/test-glob
519519
520520
521521
## Converting `.gitignore` to `ignore-glob`
522522
523523
Many other version control systems handle the specific case of
524524
--- www/globs.md
+++ www/globs.md
@@ -242,29 +242,29 @@
242 The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that
243 implement or support with web servers provide a mechanism to name some
244 files to serve with static content where a list of glob patterns
245 specifies what content may be served.
246
247 [`add`]: /help?cmd=add
248 [`addremove`]: /help?cmd=addremove
249 [`changes`]: /help?cmd=changes
250 [`clean`]: /help?cmd=clean
251 [`commit`]: /help?cmd=commit
252 [`extras`]: /help?cmd=extras
253 [`merge`]: /help?cmd=merge
254 [`settings`]: /help?cmd=settings
255 [`status`]: /help?cmd=status
256 [`touch`]: /help?cmd=touch
257 [`unset`]: /help?cmd=unset
258
259 [`tarball`]: /help?cmd=tarball
260 [`zip`]: /help?cmd=zip
261
262 [`http`]: /help?cmd=http
263 [`cgi`]: /help?cmd=cgi
264 [`server`]: /help?cmd=server
265 [`ui`]: /help?cmd=ui
266
267
268 ### Web Pages that Refer to Globs
269
270 The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that
@@ -277,13 +277,13 @@
277 The pages [`/tarball`][] and [`/zip`][] generate compressed archives
278 of a specific checkin. They may be further restricted by query
279 parameters that specify glob patterns that name files to include or
280 exclude rather than taking the entire checkin.
281
282 [`/timeline`]: /help?cmd=/timeline
283 [`/tarball`]: /help?cmd=/tarball
284 [`/zip`]: /help?cmd=/zip
285
286
287 ## Platform Quirks
288
289 Fossil glob patterns are based on the glob pattern feature of POSIX
@@ -512,12 +512,12 @@
512 C:\> echo * | fossil test-echo setting crlf-glob --args -
513
514 The [`test-glob`][] command is also handy to test if a string
515 matches a glob pattern.
516
517 [`test-echo`]: /help?cmd=test-echo
518 [`test-glob`]: /help?cmd=test-glob
519
520
521 ## Converting `.gitignore` to `ignore-glob`
522
523 Many other version control systems handle the specific case of
524
--- www/globs.md
+++ www/globs.md
@@ -242,29 +242,29 @@
242 The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that
243 implement or support with web servers provide a mechanism to name some
244 files to serve with static content where a list of glob patterns
245 specifies what content may be served.
246
247 [`add`]: /help/add
248 [`addremove`]: /help/addremove
249 [`changes`]: /help/changes
250 [`clean`]: /help/clean
251 [`commit`]: /help/commit
252 [`extras`]: /help/extras
253 [`merge`]: /help/merge
254 [`settings`]: /help/settings
255 [`status`]: /help/status
256 [`touch`]: /help/touch
257 [`unset`]: /help/unset
258
259 [`tarball`]: /help/tarball
260 [`zip`]: /help/zip
261
262 [`http`]: /help/http
263 [`cgi`]: /help/cgi
264 [`server`]: /help/server
265 [`ui`]: /help/ui
266
267
268 ### Web Pages that Refer to Globs
269
270 The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that
@@ -277,13 +277,13 @@
277 The pages [`/tarball`][] and [`/zip`][] generate compressed archives
278 of a specific checkin. They may be further restricted by query
279 parameters that specify glob patterns that name files to include or
280 exclude rather than taking the entire checkin.
281
282 [`/timeline`]: /help/www/timeline
283 [`/tarball`]: /help/www/tarball
284 [`/zip`]: /help/www/zip
285
286
287 ## Platform Quirks
288
289 Fossil glob patterns are based on the glob pattern feature of POSIX
@@ -512,12 +512,12 @@
512 C:\> echo * | fossil test-echo setting crlf-glob --args -
513
514 The [`test-glob`][] command is also handy to test if a string
515 matches a glob pattern.
516
517 [`test-echo`]: /help/test-echo
518 [`test-glob`]: /help/test-glob
519
520
521 ## Converting `.gitignore` to `ignore-glob`
522
523 Many other version control systems handle the specific case of
524
+18 -18
--- www/glossary.md
+++ www/glossary.md
@@ -90,17 +90,17 @@
9090
As a counterexample, a project tracking your [Vim] configuration
9191
history is a much better use of Fossil, because it’s all held within
9292
`~/.vim`, and your user has full rights to that subdirectory.
9393
9494
[AIF]: https://docs.asciidoctor.org/asciidoc/latest/directives/include/
95
-[IGS]: /help?cmd=ignore-glob
95
+[IGS]: /help/ignore-glob
9696
[IFRS]: ./image-format-vs-repo-size.md
97
-[tarball]: /help?cmd=tarball
98
-[tw]: /help?cmd=/tarball
97
+[tarball]: /help/tarball
98
+[tw]: /help/www/tarball
9999
[Vim]: https://www.vim.org/
100
-[zip]: /help?cmd=zip
101
-[zw]: /help?cmd=/zip
100
+[zip]: /help/zip
101
+[zw]: /help/www/zip
102102
103103
104104
## Repository <a id="repository" name="repo"></a>
105105
106106
A single file that contains all historical versions of all files in a
@@ -195,18 +195,18 @@
195195
line dotted right until even with previous line.end
196196
move right 0.05
197197
box invis "clones of Fossil itself, SQLite, etc." ljust
198198
```
199199
200
-[asdis]: /help?cmd=autosync
200
+[asdis]: /help/autosync
201201
[backup]: ./backup.md
202202
[CAP]: ./cap-theorem.md
203
-[cloned]: /help?cmd=clone
204
-[pull]: /help?cmd=pull
205
-[push]: /help?cmd=push
206
-[svrcmd]: /help?cmd=server
207
-[sync]: /help?cmd=sync
203
+[cloned]: /help/clone
204
+[pull]: /help/pull
205
+[push]: /help/push
206
+[svrcmd]: /help/server
207
+[sync]: /help/sync
208208
209209
[repository]: #repo
210210
[repositories]: #repo
211211
212212
@@ -310,15 +310,15 @@
310310
at the time of the snapshot. (Thus [the `extras` command][extras].)
311311
Contrast a snapshot taken by a virtual machine system or a
312312
[snapshotting file system][snfs], which captures changes to everything
313313
on the managed storage volume.
314314
315
-[add]: /help?cmd=add
315
+[add]: /help/add
316316
[ciname]: ./checkin_names.wiki
317
-[extras]: /help?cmd=extras
318
-[stash]: /help?cmd=stash
319
-[undo]: /help?cmd=undo
317
+[extras]: /help/extras
318
+[stash]: /help/stash
319
+[undo]: /help/undo
320320
321321
322322
323323
## Check-out <a id="check-out" name="co"></a>
324324
@@ -365,17 +365,17 @@
365365
this is because VSCode’s version control features assume it’s being
366366
used with Git, where the repository is the `.git` subdirectory
367367
contents. With Fossil, [different check-out workflows][cwork] are
368368
preferred.
369369
370
-[commit]: /help?cmd=commit
370
+[commit]: /help/commit
371371
[cwork]: ./ckout-workflows.md
372372
[h2cflp]: https://www.sqlite.org/howtocorrupt.html#_file_locking_problems
373373
[fpvsc]: https://marketplace.visualstudio.com/items?itemName=koog1000.fossil
374
-[open]: /help?cmd=open
374
+[open]: /help/open
375375
[mwd]: ./ckout-workflows.md#mcw
376
-[update]: /help?cmd=update
376
+[update]: /help/update
377377
378378
379379
## <a id="docs"></a>Embedded Documentation
380380
381381
Serving as an alternative to Fossil’s built-in [wiki], the [embedded
382382
--- www/glossary.md
+++ www/glossary.md
@@ -90,17 +90,17 @@
90 As a counterexample, a project tracking your [Vim] configuration
91 history is a much better use of Fossil, because it’s all held within
92 `~/.vim`, and your user has full rights to that subdirectory.
93
94 [AIF]: https://docs.asciidoctor.org/asciidoc/latest/directives/include/
95 [IGS]: /help?cmd=ignore-glob
96 [IFRS]: ./image-format-vs-repo-size.md
97 [tarball]: /help?cmd=tarball
98 [tw]: /help?cmd=/tarball
99 [Vim]: https://www.vim.org/
100 [zip]: /help?cmd=zip
101 [zw]: /help?cmd=/zip
102
103
104 ## Repository <a id="repository" name="repo"></a>
105
106 A single file that contains all historical versions of all files in a
@@ -195,18 +195,18 @@
195 line dotted right until even with previous line.end
196 move right 0.05
197 box invis "clones of Fossil itself, SQLite, etc." ljust
198 ```
199
200 [asdis]: /help?cmd=autosync
201 [backup]: ./backup.md
202 [CAP]: ./cap-theorem.md
203 [cloned]: /help?cmd=clone
204 [pull]: /help?cmd=pull
205 [push]: /help?cmd=push
206 [svrcmd]: /help?cmd=server
207 [sync]: /help?cmd=sync
208
209 [repository]: #repo
210 [repositories]: #repo
211
212
@@ -310,15 +310,15 @@
310 at the time of the snapshot. (Thus [the `extras` command][extras].)
311 Contrast a snapshot taken by a virtual machine system or a
312 [snapshotting file system][snfs], which captures changes to everything
313 on the managed storage volume.
314
315 [add]: /help?cmd=add
316 [ciname]: ./checkin_names.wiki
317 [extras]: /help?cmd=extras
318 [stash]: /help?cmd=stash
319 [undo]: /help?cmd=undo
320
321
322
323 ## Check-out <a id="check-out" name="co"></a>
324
@@ -365,17 +365,17 @@
365 this is because VSCode’s version control features assume it’s being
366 used with Git, where the repository is the `.git` subdirectory
367 contents. With Fossil, [different check-out workflows][cwork] are
368 preferred.
369
370 [commit]: /help?cmd=commit
371 [cwork]: ./ckout-workflows.md
372 [h2cflp]: https://www.sqlite.org/howtocorrupt.html#_file_locking_problems
373 [fpvsc]: https://marketplace.visualstudio.com/items?itemName=koog1000.fossil
374 [open]: /help?cmd=open
375 [mwd]: ./ckout-workflows.md#mcw
376 [update]: /help?cmd=update
377
378
379 ## <a id="docs"></a>Embedded Documentation
380
381 Serving as an alternative to Fossil’s built-in [wiki], the [embedded
382
--- www/glossary.md
+++ www/glossary.md
@@ -90,17 +90,17 @@
90 As a counterexample, a project tracking your [Vim] configuration
91 history is a much better use of Fossil, because it’s all held within
92 `~/.vim`, and your user has full rights to that subdirectory.
93
94 [AIF]: https://docs.asciidoctor.org/asciidoc/latest/directives/include/
95 [IGS]: /help/ignore-glob
96 [IFRS]: ./image-format-vs-repo-size.md
97 [tarball]: /help/tarball
98 [tw]: /help/www/tarball
99 [Vim]: https://www.vim.org/
100 [zip]: /help/zip
101 [zw]: /help/www/zip
102
103
104 ## Repository <a id="repository" name="repo"></a>
105
106 A single file that contains all historical versions of all files in a
@@ -195,18 +195,18 @@
195 line dotted right until even with previous line.end
196 move right 0.05
197 box invis "clones of Fossil itself, SQLite, etc." ljust
198 ```
199
200 [asdis]: /help/autosync
201 [backup]: ./backup.md
202 [CAP]: ./cap-theorem.md
203 [cloned]: /help/clone
204 [pull]: /help/pull
205 [push]: /help/push
206 [svrcmd]: /help/server
207 [sync]: /help/sync
208
209 [repository]: #repo
210 [repositories]: #repo
211
212
@@ -310,15 +310,15 @@
310 at the time of the snapshot. (Thus [the `extras` command][extras].)
311 Contrast a snapshot taken by a virtual machine system or a
312 [snapshotting file system][snfs], which captures changes to everything
313 on the managed storage volume.
314
315 [add]: /help/add
316 [ciname]: ./checkin_names.wiki
317 [extras]: /help/extras
318 [stash]: /help/stash
319 [undo]: /help/undo
320
321
322
323 ## Check-out <a id="check-out" name="co"></a>
324
@@ -365,17 +365,17 @@
365 this is because VSCode’s version control features assume it’s being
366 used with Git, where the repository is the `.git` subdirectory
367 contents. With Fossil, [different check-out workflows][cwork] are
368 preferred.
369
370 [commit]: /help/commit
371 [cwork]: ./ckout-workflows.md
372 [h2cflp]: https://www.sqlite.org/howtocorrupt.html#_file_locking_problems
373 [fpvsc]: https://marketplace.visualstudio.com/items?itemName=koog1000.fossil
374 [open]: /help/open
375 [mwd]: ./ckout-workflows.md#mcw
376 [update]: /help/update
377
378
379 ## <a id="docs"></a>Embedded Documentation
380
381 Serving as an alternative to Fossil’s built-in [wiki], the [embedded
382
--- www/grep.md
+++ www/grep.md
@@ -99,10 +99,15 @@
9999
There are several restrictions in Fossil `grep` relative to a fully
100100
POSIX compatible regular expression engine. Among them are:
101101
102102
* There is currently no support for POSIX character classes such as
103103
`[:lower:]`.
104
+
105
+* The values of `p` and `q` in the "`{p,q}`" syntax can be no greater
106
+ than 999. This is because the NFA that is used for regular expression
107
+ matching is proportional in size to the largest p or q value, and hence
108
+ allowing arbitrarily large values could result in a DoS attack.
104109
105110
* Fossil `grep` does not currently attempt to take your operating
106111
system's locale settings into account when doing this match. Since
107112
Fossil has no way to mark a given file as having a particular
108113
encoding, Fossil `grep` assumes the input files are in UTF-8 format.
109114
--- www/grep.md
+++ www/grep.md
@@ -99,10 +99,15 @@
99 There are several restrictions in Fossil `grep` relative to a fully
100 POSIX compatible regular expression engine. Among them are:
101
102 * There is currently no support for POSIX character classes such as
103 `[:lower:]`.
 
 
 
 
 
104
105 * Fossil `grep` does not currently attempt to take your operating
106 system's locale settings into account when doing this match. Since
107 Fossil has no way to mark a given file as having a particular
108 encoding, Fossil `grep` assumes the input files are in UTF-8 format.
109
--- www/grep.md
+++ www/grep.md
@@ -99,10 +99,15 @@
99 There are several restrictions in Fossil `grep` relative to a fully
100 POSIX compatible regular expression engine. Among them are:
101
102 * There is currently no support for POSIX character classes such as
103 `[:lower:]`.
104
105 * The values of `p` and `q` in the "`{p,q}`" syntax can be no greater
106 than 999. This is because the NFA that is used for regular expression
107 matching is proportional in size to the largest p or q value, and hence
108 allowing arbitrarily large values could result in a DoS attack.
109
110 * Fossil `grep` does not currently attempt to take your operating
111 system's locale settings into account when doing this match. Since
112 Fossil has no way to mark a given file as having a particular
113 encoding, Fossil `grep` assumes the input files are in UTF-8 format.
114
+2 -2
--- www/hashes.md
+++ www/hashes.md
@@ -145,10 +145,10 @@
145145
[ctkt]: ./custom_ticket.wiki
146146
[hpol]: ./hashpolicy.wiki
147147
[japi]: ./json-api/
148148
[jart]: ./json-api/api-artifact.md
149149
[jtim]: ./json-api/api-timeline.md
150
-[mset]: /help?cmd=manifest
150
+[mset]: /help/manifest
151151
[th1]: ./th1.md
152
-[trss]: /help?cmd=/timeline.rss
152
+[trss]: /help/www/timeline.rss
153153
[tvb]: ./branching.wiki
154154
[uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier
155155
--- www/hashes.md
+++ www/hashes.md
@@ -145,10 +145,10 @@
145 [ctkt]: ./custom_ticket.wiki
146 [hpol]: ./hashpolicy.wiki
147 [japi]: ./json-api/
148 [jart]: ./json-api/api-artifact.md
149 [jtim]: ./json-api/api-timeline.md
150 [mset]: /help?cmd=manifest
151 [th1]: ./th1.md
152 [trss]: /help?cmd=/timeline.rss
153 [tvb]: ./branching.wiki
154 [uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier
155
--- www/hashes.md
+++ www/hashes.md
@@ -145,10 +145,10 @@
145 [ctkt]: ./custom_ticket.wiki
146 [hpol]: ./hashpolicy.wiki
147 [japi]: ./json-api/
148 [jart]: ./json-api/api-artifact.md
149 [jtim]: ./json-api/api-timeline.md
150 [mset]: /help/manifest
151 [th1]: ./th1.md
152 [trss]: /help/www/timeline.rss
153 [tvb]: ./branching.wiki
154 [uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier
155
--- www/hashpolicy.wiki
+++ www/hashpolicy.wiki
@@ -158,11 +158,11 @@
158158
159159
When a new repository is created by cloning, the hash policy is copied
160160
from the parent.
161161
162162
For new repositories created using the
163
-[/help?cmd=new|fossil new] command the default hash policy is "sha3".
163
+[/help/new|fossil new] command the default hash policy is "sha3".
164164
That means new repositories
165165
will normally hold nothing except SHA3 hashes. The hash policy for new
166166
repositories can be overridden using the "--sha1" option to the
167167
"fossil new" command.
168168
169169
--- www/hashpolicy.wiki
+++ www/hashpolicy.wiki
@@ -158,11 +158,11 @@
158
159 When a new repository is created by cloning, the hash policy is copied
160 from the parent.
161
162 For new repositories created using the
163 [/help?cmd=new|fossil new] command the default hash policy is "sha3".
164 That means new repositories
165 will normally hold nothing except SHA3 hashes. The hash policy for new
166 repositories can be overridden using the "--sha1" option to the
167 "fossil new" command.
168
169
--- www/hashpolicy.wiki
+++ www/hashpolicy.wiki
@@ -158,11 +158,11 @@
158
159 When a new repository is created by cloning, the hash policy is copied
160 from the parent.
161
162 For new repositories created using the
163 [/help/new|fossil new] command the default hash policy is "sha3".
164 That means new repositories
165 will normally hold nothing except SHA3 hashes. The hash policy for new
166 repositories can be overridden using the "--sha1" option to the
167 "fossil new" command.
168
169
+7 -7
--- www/hints.wiki
+++ www/hints.wiki
@@ -3,11 +3,11 @@
33
A collection of useful hints and tricks in no particular order:
44
55
1. Click on two nodes of any timeline graph in succession
66
to see a diff between the two versions.
77
8
- 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands
8
+ 2. Add the "--tk" option to "[/help/diff | fossil diff]" commands
99
to get a pop-up
1010
window containing a complete side-by-side diff. (NB: The pop-up
1111
window is run as a separate Tcl/Tk process, so you will need to
1212
have Tcl/Tk installed on your machine for this to work. Visit
1313
[http://www.activestate.com/activetcl] for a quick download of
@@ -17,13 +17,13 @@
1717
alternative to "make clean". You can use "[/help/clean | fossil clean -f]"
1818
as a slightly safer alternative if the "ignore-glob" setting is
1919
not set. WARNING: make sure you did a "fossil add" for all source-files
2020
you plan to commit, otherwise those files will be deleted without warning.
2121
22
- 4. Use "[/help?cmd=all | fossil all changes]" to look for any uncommitted
22
+ 4. Use "[/help/all | fossil all changes]" to look for any uncommitted
2323
edits in any of your Fossil projects. Use
24
- "[/help?cmd=all | fossil all pull]" on your laptop
24
+ "[/help/all | fossil all pull]" on your laptop
2525
prior to going off network (for example, on a long plane ride)
2626
to make sure you have all the latest content locally. Then run
2727
"[/help/all|fossil all push]" when you get back online to upload
2828
your changes.
2929
@@ -37,13 +37,13 @@
3737
on in the Fossil repository on 2008-01-01, visit
3838
[/timeline?c=2008-01-01].
3939
4040
7. Further to the previous two hints, there are lots of query parameters
4141
that you can add to timeline pages. The available query parameters
42
- are tersely documented [/help?cmd=/timeline | here].
42
+ are tersely documented [/help/www/timeline | here].
4343
44
- 8. You can run "[/help?cmd=xdiff | fossil xdiff --tk $file1 $file2]"
44
+ 8. You can run "[/help/xdiff | fossil xdiff --tk $file1 $file2]"
4545
to get a Tk pop-up window with side-by-side diffs of two files, even if
4646
neither of the two files is part of any Fossil repository. Note that
4747
this command is "xdiff", not "diff". Change <nobr>--tk</nobr> to
4848
<nobr>--by</nobr> to see the diff in your web browser.
4949
@@ -61,14 +61,14 @@
6161
10. When editing documentation to be checked in as managed files, you can
6262
preview what the documentation will look like by using the special
6363
"ckout" branch name in the "doc" URL while running "fossil ui".
6464
See the [./embeddeddoc.wiki | embedded documentation] for details.
6565
66
- 11. Use the "[/help?cmd=ui|fossil ui /]" command to bring up a menu of
66
+ 11. Use the "[/help/ui|fossil ui /]" command to bring up a menu of
6767
all of your local Fossil repositories in your web browser.
6868
6969
12. If you have a bunch of Fossil repositories living on a remote machine
7070
that you are able to access using ssh using a command like
7171
"ssh login@remote", then you can bring up a user interface for all
7272
those remote repositories using the command:
73
- "[/help?cmd=ui|fossil ui login@remote:/]". This works by tunneling
73
+ "[/help/ui|fossil ui login@remote:/]". This works by tunneling
7474
all HTTP traffic through SSH to the remote machine.
7575
--- www/hints.wiki
+++ www/hints.wiki
@@ -3,11 +3,11 @@
3 A collection of useful hints and tricks in no particular order:
4
5 1. Click on two nodes of any timeline graph in succession
6 to see a diff between the two versions.
7
8 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands
9 to get a pop-up
10 window containing a complete side-by-side diff. (NB: The pop-up
11 window is run as a separate Tcl/Tk process, so you will need to
12 have Tcl/Tk installed on your machine for this to work. Visit
13 [http://www.activestate.com/activetcl] for a quick download of
@@ -17,13 +17,13 @@
17 alternative to "make clean". You can use "[/help/clean | fossil clean -f]"
18 as a slightly safer alternative if the "ignore-glob" setting is
19 not set. WARNING: make sure you did a "fossil add" for all source-files
20 you plan to commit, otherwise those files will be deleted without warning.
21
22 4. Use "[/help?cmd=all | fossil all changes]" to look for any uncommitted
23 edits in any of your Fossil projects. Use
24 "[/help?cmd=all | fossil all pull]" on your laptop
25 prior to going off network (for example, on a long plane ride)
26 to make sure you have all the latest content locally. Then run
27 "[/help/all|fossil all push]" when you get back online to upload
28 your changes.
29
@@ -37,13 +37,13 @@
37 on in the Fossil repository on 2008-01-01, visit
38 [/timeline?c=2008-01-01].
39
40 7. Further to the previous two hints, there are lots of query parameters
41 that you can add to timeline pages. The available query parameters
42 are tersely documented [/help?cmd=/timeline | here].
43
44 8. You can run "[/help?cmd=xdiff | fossil xdiff --tk $file1 $file2]"
45 to get a Tk pop-up window with side-by-side diffs of two files, even if
46 neither of the two files is part of any Fossil repository. Note that
47 this command is "xdiff", not "diff". Change <nobr>--tk</nobr> to
48 <nobr>--by</nobr> to see the diff in your web browser.
49
@@ -61,14 +61,14 @@
61 10. When editing documentation to be checked in as managed files, you can
62 preview what the documentation will look like by using the special
63 "ckout" branch name in the "doc" URL while running "fossil ui".
64 See the [./embeddeddoc.wiki | embedded documentation] for details.
65
66 11. Use the "[/help?cmd=ui|fossil ui /]" command to bring up a menu of
67 all of your local Fossil repositories in your web browser.
68
69 12. If you have a bunch of Fossil repositories living on a remote machine
70 that you are able to access using ssh using a command like
71 "ssh login@remote", then you can bring up a user interface for all
72 those remote repositories using the command:
73 "[/help?cmd=ui|fossil ui login@remote:/]". This works by tunneling
74 all HTTP traffic through SSH to the remote machine.
75
--- www/hints.wiki
+++ www/hints.wiki
@@ -3,11 +3,11 @@
3 A collection of useful hints and tricks in no particular order:
4
5 1. Click on two nodes of any timeline graph in succession
6 to see a diff between the two versions.
7
8 2. Add the "--tk" option to "[/help/diff | fossil diff]" commands
9 to get a pop-up
10 window containing a complete side-by-side diff. (NB: The pop-up
11 window is run as a separate Tcl/Tk process, so you will need to
12 have Tcl/Tk installed on your machine for this to work. Visit
13 [http://www.activestate.com/activetcl] for a quick download of
@@ -17,13 +17,13 @@
17 alternative to "make clean". You can use "[/help/clean | fossil clean -f]"
18 as a slightly safer alternative if the "ignore-glob" setting is
19 not set. WARNING: make sure you did a "fossil add" for all source-files
20 you plan to commit, otherwise those files will be deleted without warning.
21
22 4. Use "[/help/all | fossil all changes]" to look for any uncommitted
23 edits in any of your Fossil projects. Use
24 "[/help/all | fossil all pull]" on your laptop
25 prior to going off network (for example, on a long plane ride)
26 to make sure you have all the latest content locally. Then run
27 "[/help/all|fossil all push]" when you get back online to upload
28 your changes.
29
@@ -37,13 +37,13 @@
37 on in the Fossil repository on 2008-01-01, visit
38 [/timeline?c=2008-01-01].
39
40 7. Further to the previous two hints, there are lots of query parameters
41 that you can add to timeline pages. The available query parameters
42 are tersely documented [/help/www/timeline | here].
43
44 8. You can run "[/help/xdiff | fossil xdiff --tk $file1 $file2]"
45 to get a Tk pop-up window with side-by-side diffs of two files, even if
46 neither of the two files is part of any Fossil repository. Note that
47 this command is "xdiff", not "diff". Change <nobr>--tk</nobr> to
48 <nobr>--by</nobr> to see the diff in your web browser.
49
@@ -61,14 +61,14 @@
61 10. When editing documentation to be checked in as managed files, you can
62 preview what the documentation will look like by using the special
63 "ckout" branch name in the "doc" URL while running "fossil ui".
64 See the [./embeddeddoc.wiki | embedded documentation] for details.
65
66 11. Use the "[/help/ui|fossil ui /]" command to bring up a menu of
67 all of your local Fossil repositories in your web browser.
68
69 12. If you have a bunch of Fossil repositories living on a remote machine
70 that you are able to access using ssh using a command like
71 "ssh login@remote", then you can bring up a user interface for all
72 those remote repositories using the command:
73 "[/help/ui|fossil ui login@remote:/]". This works by tunneling
74 all HTTP traffic through SSH to the remote machine.
75
+4 -4
--- www/index.wiki
+++ www/index.wiki
@@ -84,16 +84,16 @@
8484
the repository are consistent prior to each commit.
8585
8686
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
8787
8888
<hr>
89
-<h3>Latest Release: 2.26 ([/timeline?c=version-2.26|2025-04-30])</h3>
89
+<h3>Latest Release: 2.27 ([/timeline?c=version-2.27|2025-09-30])</h3>
9090
9191
* [/uv/download.html|Download]
92
- * [./changes.wiki#v2_26|Change Summary]
93
- * [/timeline?p=version-2.26&d=version-2.25&y=ci|Check-ins in version 2.26]
94
- * [/timeline?df=version-2.26&y=ci|Check-ins derived from the 2.26 release]
92
+ * [./changes.wiki#v2_27|Change Summary]
93
+ * [/timeline?p=version-2.27&d=version-2.26&y=ci|Check-ins in version 2.27]
94
+ * [/timeline?df=version-2.27&y=ci|Check-ins derived from the 2.27 release]
9595
* [/timeline?t=release|Timeline of all past releases]
9696
9797
<hr>
9898
<h3>Quick Start</h3>
9999
100100
--- www/index.wiki
+++ www/index.wiki
@@ -84,16 +84,16 @@
84 the repository are consistent prior to each commit.
85
86 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
87
88 <hr>
89 <h3>Latest Release: 2.26 ([/timeline?c=version-2.26|2025-04-30])</h3>
90
91 * [/uv/download.html|Download]
92 * [./changes.wiki#v2_26|Change Summary]
93 * [/timeline?p=version-2.26&d=version-2.25&y=ci|Check-ins in version 2.26]
94 * [/timeline?df=version-2.26&y=ci|Check-ins derived from the 2.26 release]
95 * [/timeline?t=release|Timeline of all past releases]
96
97 <hr>
98 <h3>Quick Start</h3>
99
100
--- www/index.wiki
+++ www/index.wiki
@@ -84,16 +84,16 @@
84 the repository are consistent prior to each commit.
85
86 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
87
88 <hr>
89 <h3>Latest Release: 2.27 ([/timeline?c=version-2.27|2025-09-30])</h3>
90
91 * [/uv/download.html|Download]
92 * [./changes.wiki#v2_27|Change Summary]
93 * [/timeline?p=version-2.27&d=version-2.26&y=ci|Check-ins in version 2.27]
94 * [/timeline?df=version-2.27&y=ci|Check-ins derived from the 2.27 release]
95 * [/timeline?t=release|Timeline of all past releases]
96
97 <hr>
98 <h3>Quick Start</h3>
99
100
+1 -1
--- www/inout.wiki
+++ www/inout.wiki
@@ -27,11 +27,11 @@
2727
2828
<a id="fx_git"></a>
2929
Note that in new imports, Fossil defaults to using the email component of the
3030
Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is
3131
passed) to attribute check-ins in the imported repository. Alternatively, the
32
-[/help?cmd=import | <code>--attribute</code>] option can be passed to have all
32
+[/help/import | <code>--attribute</code>] option can be passed to have all
3333
commits by a given committer attributed to a desired username. This will create
3434
and populate the new <code>fx_git</code> table in the repository database to
3535
maintain a record of correspondent usernames and email addresses that can be
3636
used in subsequent exports or incremental imports.
3737
3838
--- www/inout.wiki
+++ www/inout.wiki
@@ -27,11 +27,11 @@
27
28 <a id="fx_git"></a>
29 Note that in new imports, Fossil defaults to using the email component of the
30 Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is
31 passed) to attribute check-ins in the imported repository. Alternatively, the
32 [/help?cmd=import | <code>--attribute</code>] option can be passed to have all
33 commits by a given committer attributed to a desired username. This will create
34 and populate the new <code>fx_git</code> table in the repository database to
35 maintain a record of correspondent usernames and email addresses that can be
36 used in subsequent exports or incremental imports.
37
38
--- www/inout.wiki
+++ www/inout.wiki
@@ -27,11 +27,11 @@
27
28 <a id="fx_git"></a>
29 Note that in new imports, Fossil defaults to using the email component of the
30 Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is
31 passed) to attribute check-ins in the imported repository. Alternatively, the
32 [/help/import | <code>--attribute</code>] option can be passed to have all
33 commits by a given committer attributed to a desired username. This will create
34 and populate the new <code>fx_git</code> table in the repository database to
35 maintain a record of correspondent usernames and email addresses that can be
36 used in subsequent exports or incremental imports.
37
38
--- www/interwiki.md
+++ www/interwiki.md
@@ -64,11 +64,11 @@
6464
6565
The intermap defines a mapping from interwiki Tags to full URLs. The
6666
Intermap can be viewed and managed using the [fossil interwiki][iwiki]
6767
command or the [/intermap][imap] webpage.
6868
69
-[iwiki]: /help?cmd=interwiki
69
+[iwiki]: /help/interwiki
7070
[imap]: /intermap
7171
7272
The current intermap for a server is seen on the [/intermap][imap] page
7373
(which is read-only for non-Setup users) and at the bottom of the built-in
7474
[Fossil Wiki rules](/wiki_rules) and [Markdown rules](/md_rules)
@@ -100,11 +100,11 @@
100100
"_source&rarr;target_", but it also tracks "_target&rarr;source_".
101101
But backtracking does not work for interwiki links, since the Fossil
102102
running on the target has no way of scanning the source text and
103103
hence has no way of knowing that it is a target of a link from the source.
104104
105
-[fcfg]: /help?cmd=config
105
+[fcfg]: /help/config
106106
107107
## Intermap Storage Details
108108
109109
The intermap is stored in the CONFIG table of the repository database,
110110
in entries with names of the form "<tt>interwiki:</tt><i>Tag</i>". The
111111
--- www/interwiki.md
+++ www/interwiki.md
@@ -64,11 +64,11 @@
64
65 The intermap defines a mapping from interwiki Tags to full URLs. The
66 Intermap can be viewed and managed using the [fossil interwiki][iwiki]
67 command or the [/intermap][imap] webpage.
68
69 [iwiki]: /help?cmd=interwiki
70 [imap]: /intermap
71
72 The current intermap for a server is seen on the [/intermap][imap] page
73 (which is read-only for non-Setup users) and at the bottom of the built-in
74 [Fossil Wiki rules](/wiki_rules) and [Markdown rules](/md_rules)
@@ -100,11 +100,11 @@
100 "_source&rarr;target_", but it also tracks "_target&rarr;source_".
101 But backtracking does not work for interwiki links, since the Fossil
102 running on the target has no way of scanning the source text and
103 hence has no way of knowing that it is a target of a link from the source.
104
105 [fcfg]: /help?cmd=config
106
107 ## Intermap Storage Details
108
109 The intermap is stored in the CONFIG table of the repository database,
110 in entries with names of the form "<tt>interwiki:</tt><i>Tag</i>". The
111
--- www/interwiki.md
+++ www/interwiki.md
@@ -64,11 +64,11 @@
64
65 The intermap defines a mapping from interwiki Tags to full URLs. The
66 Intermap can be viewed and managed using the [fossil interwiki][iwiki]
67 command or the [/intermap][imap] webpage.
68
69 [iwiki]: /help/interwiki
70 [imap]: /intermap
71
72 The current intermap for a server is seen on the [/intermap][imap] page
73 (which is read-only for non-Setup users) and at the bottom of the built-in
74 [Fossil Wiki rules](/wiki_rules) and [Markdown rules](/md_rules)
@@ -100,11 +100,11 @@
100 "_source&rarr;target_", but it also tracks "_target&rarr;source_".
101 But backtracking does not work for interwiki links, since the Fossil
102 running on the target has no way of scanning the source text and
103 hence has no way of knowing that it is a target of a link from the source.
104
105 [fcfg]: /help/config
106
107 ## Intermap Storage Details
108
109 The intermap is stored in the CONFIG table of the repository database,
110 in entries with names of the form "<tt>interwiki:</tt><i>Tag</i>". The
111
--- www/javascript.md
+++ www/javascript.md
@@ -263,17 +263,17 @@
263263
[ciu]: https://caniuse.com/
264264
[cskin]: ./customskin.md
265265
[dcsp]: ./defcsp.md
266266
[es2015]: https://ecma-international.org/ecma-262/6.0/
267267
[es6dep]: https://caniuse.com/#feat=es6
268
-[fcgi]: /help?cmd=cgi
268
+[fcgi]: /help/cgi
269269
[ffor]: https://fossil-scm.org/forum/
270270
[flic]: /doc/trunk/COPYRIGHT-BSD2.txt
271271
[fshome]: /doc/trunk/www/server/
272272
[fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable
273273
[fsrc]: https://fossil-scm.org/home/file/src
274
-[fsrv]: /help?cmd=server
274
+[fsrv]: /help/server
275275
[hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca
276276
[ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666
277277
[ns]: https://noscript.net/
278278
[pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d
279279
[s1]: https://blockmetry.com/blog/javascript-disabled
@@ -390,11 +390,11 @@
390390
```
391391
392392
Extending this concept to other text editors is an exercise left to the
393393
reader.
394394
395
-[fwc]: /help?cmd=wiki
395
+[fwc]: /help/wiki
396396
[fwt]: ./wikitheory.wiki
397397
398398
399399
### <a id="fedit"></a>The File Editor
400400
@@ -430,11 +430,11 @@
430430
When viewing source files, Fossil offers to show line numbers in some
431431
cases. ([Example][mainc].) Toggling them on and off is currently handled
432432
in JavaScript, rather than forcing a page-reload via a button click.
433433
434434
_Workaround:_ Manually edit the URL to give the “`ln`” query parameter
435
-per [the `/file` docs](/help?cmd=/file).
435
+per [the `/file` docs](/help/www/file).
436436
437437
_Potential Better Workaround:_ Someone sufficiently interested could
438438
[provide a patch][cg] to add a `<noscript>` wrapped HTML button that
439439
would reload the page with this parameter included/excluded to implement
440440
the toggle via a server round-trip.
441441
--- www/javascript.md
+++ www/javascript.md
@@ -263,17 +263,17 @@
263 [ciu]: https://caniuse.com/
264 [cskin]: ./customskin.md
265 [dcsp]: ./defcsp.md
266 [es2015]: https://ecma-international.org/ecma-262/6.0/
267 [es6dep]: https://caniuse.com/#feat=es6
268 [fcgi]: /help?cmd=cgi
269 [ffor]: https://fossil-scm.org/forum/
270 [flic]: /doc/trunk/COPYRIGHT-BSD2.txt
271 [fshome]: /doc/trunk/www/server/
272 [fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable
273 [fsrc]: https://fossil-scm.org/home/file/src
274 [fsrv]: /help?cmd=server
275 [hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca
276 [ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666
277 [ns]: https://noscript.net/
278 [pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d
279 [s1]: https://blockmetry.com/blog/javascript-disabled
@@ -390,11 +390,11 @@
390 ```
391
392 Extending this concept to other text editors is an exercise left to the
393 reader.
394
395 [fwc]: /help?cmd=wiki
396 [fwt]: ./wikitheory.wiki
397
398
399 ### <a id="fedit"></a>The File Editor
400
@@ -430,11 +430,11 @@
430 When viewing source files, Fossil offers to show line numbers in some
431 cases. ([Example][mainc].) Toggling them on and off is currently handled
432 in JavaScript, rather than forcing a page-reload via a button click.
433
434 _Workaround:_ Manually edit the URL to give the “`ln`” query parameter
435 per [the `/file` docs](/help?cmd=/file).
436
437 _Potential Better Workaround:_ Someone sufficiently interested could
438 [provide a patch][cg] to add a `<noscript>` wrapped HTML button that
439 would reload the page with this parameter included/excluded to implement
440 the toggle via a server round-trip.
441
--- www/javascript.md
+++ www/javascript.md
@@ -263,17 +263,17 @@
263 [ciu]: https://caniuse.com/
264 [cskin]: ./customskin.md
265 [dcsp]: ./defcsp.md
266 [es2015]: https://ecma-international.org/ecma-262/6.0/
267 [es6dep]: https://caniuse.com/#feat=es6
268 [fcgi]: /help/cgi
269 [ffor]: https://fossil-scm.org/forum/
270 [flic]: /doc/trunk/COPYRIGHT-BSD2.txt
271 [fshome]: /doc/trunk/www/server/
272 [fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable
273 [fsrc]: https://fossil-scm.org/home/file/src
274 [fsrv]: /help/server
275 [hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca
276 [ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666
277 [ns]: https://noscript.net/
278 [pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d
279 [s1]: https://blockmetry.com/blog/javascript-disabled
@@ -390,11 +390,11 @@
390 ```
391
392 Extending this concept to other text editors is an exercise left to the
393 reader.
394
395 [fwc]: /help/wiki
396 [fwt]: ./wikitheory.wiki
397
398
399 ### <a id="fedit"></a>The File Editor
400
@@ -430,11 +430,11 @@
430 When viewing source files, Fossil offers to show line numbers in some
431 cases. ([Example][mainc].) Toggling them on and off is currently handled
432 in JavaScript, rather than forcing a page-reload via a button click.
433
434 _Workaround:_ Manually edit the URL to give the “`ln`” query parameter
435 per [the `/file` docs](/help/www/file).
436
437 _Potential Better Workaround:_ Someone sufficiently interested could
438 [provide a patch][cg] to add a `<noscript>` wrapped HTML button that
439 would reload the page with this parameter included/excluded to implement
440 the toggle via a server round-trip.
441
--- www/json-api/intro.md
+++ www/json-api/intro.md
@@ -146,11 +146,11 @@
146146
up the “payload” area of each HTTP request, so there is no
147147
reasonable way to include binary data in the JSON message without
148148
some sort of codec like Base64, for which there is no provision in
149149
the current JSON API. You will therefore find no JSON API for
150150
committing changes to a file in the repository, for example. Other
151
- Fossil APIs such as [`/raw`](/help?cmd=/raw) or
151
+ Fossil APIs such as [`/raw`](/help/www/raw) or
152152
[`/fileedit`](../fileedit-page.md) may serve you better.
153153
- **64-bit integers:** The JSON standard does not specify integer precision,
154154
because it targets many different platforms, and not all of
155155
them can support more than 32 bits. JavaScript (from which JSON
156156
derives) supports 53 bits of integer precision, which may affect how
157157
--- www/json-api/intro.md
+++ www/json-api/intro.md
@@ -146,11 +146,11 @@
146 up the “payload” area of each HTTP request, so there is no
147 reasonable way to include binary data in the JSON message without
148 some sort of codec like Base64, for which there is no provision in
149 the current JSON API. You will therefore find no JSON API for
150 committing changes to a file in the repository, for example. Other
151 Fossil APIs such as [`/raw`](/help?cmd=/raw) or
152 [`/fileedit`](../fileedit-page.md) may serve you better.
153 - **64-bit integers:** The JSON standard does not specify integer precision,
154 because it targets many different platforms, and not all of
155 them can support more than 32 bits. JavaScript (from which JSON
156 derives) supports 53 bits of integer precision, which may affect how
157
--- www/json-api/intro.md
+++ www/json-api/intro.md
@@ -146,11 +146,11 @@
146 up the “payload” area of each HTTP request, so there is no
147 reasonable way to include binary data in the JSON message without
148 some sort of codec like Base64, for which there is no provision in
149 the current JSON API. You will therefore find no JSON API for
150 committing changes to a file in the repository, for example. Other
151 Fossil APIs such as [`/raw`](/help/www/raw) or
152 [`/fileedit`](../fileedit-page.md) may serve you better.
153 - **64-bit integers:** The JSON standard does not specify integer precision,
154 because it targets many different platforms, and not all of
155 them can support more than 32 bits. JavaScript (from which JSON
156 derives) supports 53 bits of integer precision, which may affect how
157
+1 -1
--- www/loadmgmt.md
+++ www/loadmgmt.md
@@ -88,9 +88,9 @@
8888
system that does not support `getloadavg()` and so the load-average
8989
limiter will not function.
9090
9191
9292
[503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4
93
-[hte]: /help?cmd=/test-env
93
+[hte]: /help/www/test-env
9494
[gla]: https://linux.die.net/man/3/getloadavg
9595
[lin]: http://www.linode.com
9696
[sh]: ./selfhost.wiki
9797
--- www/loadmgmt.md
+++ www/loadmgmt.md
@@ -88,9 +88,9 @@
88 system that does not support `getloadavg()` and so the load-average
89 limiter will not function.
90
91
92 [503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4
93 [hte]: /help?cmd=/test-env
94 [gla]: https://linux.die.net/man/3/getloadavg
95 [lin]: http://www.linode.com
96 [sh]: ./selfhost.wiki
97
--- www/loadmgmt.md
+++ www/loadmgmt.md
@@ -88,9 +88,9 @@
88 system that does not support `getloadavg()` and so the load-average
89 limiter will not function.
90
91
92 [503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4
93 [hte]: /help/www/test-env
94 [gla]: https://linux.die.net/man/3/getloadavg
95 [lin]: http://www.linode.com
96 [sh]: ./selfhost.wiki
97
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -41,11 +41,11 @@
4141
All three SQLite source files are byte-for-byte copies of files by
4242
the same name in the
4343
standard [http://www.sqlite.org/amalgamation.html | amalgamation].
4444
The sqlite3.c file implements the database engine. The shell.c file
4545
implements the command-line shell, which is accessed in fossil using
46
-the [/help?cmd=sqlite3 | fossil sql] command.
46
+the [/help/sqlite3 | fossil sql] command.
4747
4848
The shell.c command-line shell uses the [https://github.com/antirez/linenoise |
4949
linenoise] library to implement line editing. linenoise comprises two
5050
source files which were copied from the upstream repository with only
5151
very minor portability edits:
@@ -72,11 +72,11 @@
7272
resource files using a small program called:
7373
7474
12 [/file/tools/mkbuiltin.c | mkbuiltin.c]
7575
7676
Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl]
77
-script used to implement the --tk option to [/help?cmd=diff| fossil diff],
77
+script used to implement the --tk option to [/help/diff| fossil diff],
7878
the [/file/src/markdown.md | markdown documentation], and the various
7979
CSS scripts, headers, and footers used to implement built-in skins. New
8080
resources files are added to the "extra_files" variable in
8181
[/file/tools/makemake.tcl | makemake.tcl].
8282
@@ -265,15 +265,15 @@
265265
* -DSQLITE_DEFAULT_FILE_FORMAT=4
266266
* -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
267267
268268
The first three symbol definitions above are required; the others are merely
269269
recommended. Extension loading is omitted as a security measure. The dbstat
270
-virtual table is needed for the [/help?cmd=/repo-tabsize|/repo-tabsize] page.
270
+virtual table is needed for the [/help/www/repo-tabsize|/repo-tabsize] page.
271271
FTS4 is needed for the search feature. Fossil is single-threaded so mutexing
272272
is disabled in SQLite as a performance enhancement. The
273273
SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries
274
-in the "[/help?cmd=sqlite3|fossil sql]" command much more readable.
274
+in the "[/help/sqlite3|fossil sql]" command much more readable.
275275
276276
When compiling the shell.c source file, these macros are required:
277277
278278
* -Dmain=sqlite3_main
279279
* -DSQLITE_OMIT_LOAD_EXTENSION=1
280280
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -41,11 +41,11 @@
41 All three SQLite source files are byte-for-byte copies of files by
42 the same name in the
43 standard [http://www.sqlite.org/amalgamation.html | amalgamation].
44 The sqlite3.c file implements the database engine. The shell.c file
45 implements the command-line shell, which is accessed in fossil using
46 the [/help?cmd=sqlite3 | fossil sql] command.
47
48 The shell.c command-line shell uses the [https://github.com/antirez/linenoise |
49 linenoise] library to implement line editing. linenoise comprises two
50 source files which were copied from the upstream repository with only
51 very minor portability edits:
@@ -72,11 +72,11 @@
72 resource files using a small program called:
73
74 12 [/file/tools/mkbuiltin.c | mkbuiltin.c]
75
76 Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl]
77 script used to implement the --tk option to [/help?cmd=diff| fossil diff],
78 the [/file/src/markdown.md | markdown documentation], and the various
79 CSS scripts, headers, and footers used to implement built-in skins. New
80 resources files are added to the "extra_files" variable in
81 [/file/tools/makemake.tcl | makemake.tcl].
82
@@ -265,15 +265,15 @@
265 * -DSQLITE_DEFAULT_FILE_FORMAT=4
266 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
267
268 The first three symbol definitions above are required; the others are merely
269 recommended. Extension loading is omitted as a security measure. The dbstat
270 virtual table is needed for the [/help?cmd=/repo-tabsize|/repo-tabsize] page.
271 FTS4 is needed for the search feature. Fossil is single-threaded so mutexing
272 is disabled in SQLite as a performance enhancement. The
273 SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries
274 in the "[/help?cmd=sqlite3|fossil sql]" command much more readable.
275
276 When compiling the shell.c source file, these macros are required:
277
278 * -Dmain=sqlite3_main
279 * -DSQLITE_OMIT_LOAD_EXTENSION=1
280
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -41,11 +41,11 @@
41 All three SQLite source files are byte-for-byte copies of files by
42 the same name in the
43 standard [http://www.sqlite.org/amalgamation.html | amalgamation].
44 The sqlite3.c file implements the database engine. The shell.c file
45 implements the command-line shell, which is accessed in fossil using
46 the [/help/sqlite3 | fossil sql] command.
47
48 The shell.c command-line shell uses the [https://github.com/antirez/linenoise |
49 linenoise] library to implement line editing. linenoise comprises two
50 source files which were copied from the upstream repository with only
51 very minor portability edits:
@@ -72,11 +72,11 @@
72 resource files using a small program called:
73
74 12 [/file/tools/mkbuiltin.c | mkbuiltin.c]
75
76 Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl]
77 script used to implement the --tk option to [/help/diff| fossil diff],
78 the [/file/src/markdown.md | markdown documentation], and the various
79 CSS scripts, headers, and footers used to implement built-in skins. New
80 resources files are added to the "extra_files" variable in
81 [/file/tools/makemake.tcl | makemake.tcl].
82
@@ -265,15 +265,15 @@
265 * -DSQLITE_DEFAULT_FILE_FORMAT=4
266 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
267
268 The first three symbol definitions above are required; the others are merely
269 recommended. Extension loading is omitted as a security measure. The dbstat
270 virtual table is needed for the [/help/www/repo-tabsize|/repo-tabsize] page.
271 FTS4 is needed for the search feature. Fossil is single-threaded so mutexing
272 is disabled in SQLite as a performance enhancement. The
273 SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries
274 in the "[/help/sqlite3|fossil sql]" command much more readable.
275
276 When compiling the shell.c source file, these macros are required:
277
278 * -Dmain=sqlite3_main
279 * -DSQLITE_OMIT_LOAD_EXTENSION=1
280
--- www/mdtest/test1.md
+++ www/mdtest/test1.md
@@ -31,11 +31,11 @@
3131
works without the $ROOT prefix. (Though: the $ROOT prefix is required
3232
for HTML documents.)
3333
3434
* Timeline: [](/timeline)
3535
36
- * Help: [](/help?cmd=help)
36
+ * Help: [](/help/help)
3737
3838
* Site-map: [](/sitemap)
3939
4040
## The Magic $CURRENT Document Version Translation
4141
4242
--- www/mdtest/test1.md
+++ www/mdtest/test1.md
@@ -31,11 +31,11 @@
31 works without the $ROOT prefix. (Though: the $ROOT prefix is required
32 for HTML documents.)
33
34 * Timeline: [](/timeline)
35
36 * Help: [](/help?cmd=help)
37
38 * Site-map: [](/sitemap)
39
40 ## The Magic $CURRENT Document Version Translation
41
42
--- www/mdtest/test1.md
+++ www/mdtest/test1.md
@@ -31,11 +31,11 @@
31 works without the $ROOT prefix. (Though: the $ROOT prefix is required
32 for HTML documents.)
33
34 * Timeline: [](/timeline)
35
36 * Help: [](/help/help)
37
38 * Site-map: [](/sitemap)
39
40 ## The Magic $CURRENT Document Version Translation
41
42
--- www/mirrorlimitations.md
+++ www/mirrorlimitations.md
@@ -1,8 +1,8 @@
11
# Limitations On Git Mirrors
22
3
-The "<tt>[fossil git export](/help?cmd=git)</tt>" command can be used to
3
+The "<tt>[fossil git export](/help/git)</tt>" command can be used to
44
mirror a Fossil repository to Git.
55
([Setup instructions](./mirrortogithub.md) and an
66
[example](https://github.com/drhsqlite/fossil-mirror).)
77
But the export to Git is not perfect. Some information is lost during
88
export due to limitations in Git. This page describes what content of
99
--- www/mirrorlimitations.md
+++ www/mirrorlimitations.md
@@ -1,8 +1,8 @@
1 # Limitations On Git Mirrors
2
3 The "<tt>[fossil git export](/help?cmd=git)</tt>" command can be used to
4 mirror a Fossil repository to Git.
5 ([Setup instructions](./mirrortogithub.md) and an
6 [example](https://github.com/drhsqlite/fossil-mirror).)
7 But the export to Git is not perfect. Some information is lost during
8 export due to limitations in Git. This page describes what content of
9
--- www/mirrorlimitations.md
+++ www/mirrorlimitations.md
@@ -1,8 +1,8 @@
1 # Limitations On Git Mirrors
2
3 The "<tt>[fossil git export](/help/git)</tt>" command can be used to
4 mirror a Fossil repository to Git.
5 ([Setup instructions](./mirrortogithub.md) and an
6 [example](https://github.com/drhsqlite/fossil-mirror).)
7 But the export to Git is not perfect. Some information is lost during
8 export due to limitations in Git. This page describes what content of
9
--- www/mirrortogithub.md
+++ www/mirrortogithub.md
@@ -130,14 +130,14 @@
130130
previously imported from Git using the [`--attribute`][attr] option, the
131131
[`fx_git`][fxgit] table will be queried for correspondent email addresses.
132132
Only if neither of these methods produce a user specified email will the
133133
abovementioned generic address be used.
134134
135
-[attr]: /help?cmd=import
135
+[attr]: /help/import
136136
[fxgit]: ./inout.wiki#fx_git
137
-[ui]: /help?cmd=ui
138
-[usercmd]: /help?cmd=user
137
+[ui]: /help/ui
138
+[usercmd]: /help/user
139139
140140
141141
## <a id='ex1'></a>Example GitHub Mirrors
142142
143143
As of this writing (2019-03-16) Fossil’s own repository is mirrored
144144
--- www/mirrortogithub.md
+++ www/mirrortogithub.md
@@ -130,14 +130,14 @@
130 previously imported from Git using the [`--attribute`][attr] option, the
131 [`fx_git`][fxgit] table will be queried for correspondent email addresses.
132 Only if neither of these methods produce a user specified email will the
133 abovementioned generic address be used.
134
135 [attr]: /help?cmd=import
136 [fxgit]: ./inout.wiki#fx_git
137 [ui]: /help?cmd=ui
138 [usercmd]: /help?cmd=user
139
140
141 ## <a id='ex1'></a>Example GitHub Mirrors
142
143 As of this writing (2019-03-16) Fossil’s own repository is mirrored
144
--- www/mirrortogithub.md
+++ www/mirrortogithub.md
@@ -130,14 +130,14 @@
130 previously imported from Git using the [`--attribute`][attr] option, the
131 [`fx_git`][fxgit] table will be queried for correspondent email addresses.
132 Only if neither of these methods produce a user specified email will the
133 abovementioned generic address be used.
134
135 [attr]: /help/import
136 [fxgit]: ./inout.wiki#fx_git
137 [ui]: /help/ui
138 [usercmd]: /help/user
139
140
141 ## <a id='ex1'></a>Example GitHub Mirrors
142
143 As of this writing (2019-03-16) Fossil’s own repository is mirrored
144
--- www/password.wiki
+++ www/password.wiki
@@ -64,11 +64,11 @@
6464
6565
<h2>Web Interface Authentication</h2>
6666
6767
When a user logs into Fossil using the web interface, the login name
6868
and password are sent in the clear to the server. For most modern fossil
69
-server setups with [/help?cmd=redirect-to-https|redirect-to-https] enabled,
69
+server setups with [/help/redirect-to-https|redirect-to-https] enabled,
7070
this will be protected by the
7171
SSL connection over HTTPS so it cannot be easily viewed. The server then
7272
hashes the password and compares it against the value stored in USER.PW.
7373
If they match, the server sets a cookie on the client to record the
7474
login. This cookie contains a large amount of high-quality randomness
7575
--- www/password.wiki
+++ www/password.wiki
@@ -64,11 +64,11 @@
64
65 <h2>Web Interface Authentication</h2>
66
67 When a user logs into Fossil using the web interface, the login name
68 and password are sent in the clear to the server. For most modern fossil
69 server setups with [/help?cmd=redirect-to-https|redirect-to-https] enabled,
70 this will be protected by the
71 SSL connection over HTTPS so it cannot be easily viewed. The server then
72 hashes the password and compares it against the value stored in USER.PW.
73 If they match, the server sets a cookie on the client to record the
74 login. This cookie contains a large amount of high-quality randomness
75
--- www/password.wiki
+++ www/password.wiki
@@ -64,11 +64,11 @@
64
65 <h2>Web Interface Authentication</h2>
66
67 When a user logs into Fossil using the web interface, the login name
68 and password are sent in the clear to the server. For most modern fossil
69 server setups with [/help/redirect-to-https|redirect-to-https] enabled,
70 this will be protected by the
71 SSL connection over HTTPS so it cannot be easily viewed. The server then
72 hashes the password and compares it against the value stored in USER.PW.
73 If they match, the server sets a cookie on the client to record the
74 login. This cookie contains a large amount of high-quality randomness
75
+1 -1
--- www/patchcmd.md
+++ www/patchcmd.md
@@ -1,8 +1,8 @@
11
# The "fossil patch" command
22
3
-The "[fossil patch](/help?cmd=patch)" command is designed to transfer
3
+The "[fossil patch](/help/patch)" command is designed to transfer
44
uncommitted changes from one check-out to another, including transfering
55
those changes to other machines.
66
77
For example, if you are working on a Windows desktop and you want to
88
test your changes on a Linux server before you commit, you can use the
99
--- www/patchcmd.md
+++ www/patchcmd.md
@@ -1,8 +1,8 @@
1 # The "fossil patch" command
2
3 The "[fossil patch](/help?cmd=patch)" command is designed to transfer
4 uncommitted changes from one check-out to another, including transfering
5 those changes to other machines.
6
7 For example, if you are working on a Windows desktop and you want to
8 test your changes on a Linux server before you commit, you can use the
9
--- www/patchcmd.md
+++ www/patchcmd.md
@@ -1,8 +1,8 @@
1 # The "fossil patch" command
2
3 The "[fossil patch](/help/patch)" command is designed to transfer
4 uncommitted changes from one check-out to another, including transfering
5 those changes to other machines.
6
7 For example, if you are working on a Windows desktop and you want to
8 test your changes on a Linux server before you commit, you can use the
9
--- www/private.wiki
+++ www/private.wiki
@@ -47,11 +47,11 @@
4747
To avoid generating a missing artifact
4848
reference on peer repositories without the private branch, the merge parent
4949
is not recorded when merging the private branch into a public branch. As a
5050
consequence, the web UI timeline does not draw a merge line from the private
5151
merge parent to the public merge child. Moreover, repeat private-to-public
52
-merge operations (without the [/help?cmd=merge | --force option]) with files
52
+merge operations (without the [/help/merge | --force option]) with files
5353
added on the private branch may only work once, but later abort with
5454
"WARNING: no common ancestor for FILE", as the parent-child relationship is
5555
not recorded. (See the [/doc/trunk/www/branching.wiki | Branching, Forking,
5656
Merging, and Tagging] document for more information.)
5757
</div>
5858
--- www/private.wiki
+++ www/private.wiki
@@ -47,11 +47,11 @@
47 To avoid generating a missing artifact
48 reference on peer repositories without the private branch, the merge parent
49 is not recorded when merging the private branch into a public branch. As a
50 consequence, the web UI timeline does not draw a merge line from the private
51 merge parent to the public merge child. Moreover, repeat private-to-public
52 merge operations (without the [/help?cmd=merge | --force option]) with files
53 added on the private branch may only work once, but later abort with
54 "WARNING: no common ancestor for FILE", as the parent-child relationship is
55 not recorded. (See the [/doc/trunk/www/branching.wiki | Branching, Forking,
56 Merging, and Tagging] document for more information.)
57 </div>
58
--- www/private.wiki
+++ www/private.wiki
@@ -47,11 +47,11 @@
47 To avoid generating a missing artifact
48 reference on peer repositories without the private branch, the merge parent
49 is not recorded when merging the private branch into a public branch. As a
50 consequence, the web UI timeline does not draw a merge line from the private
51 merge parent to the public merge child. Moreover, repeat private-to-public
52 merge operations (without the [/help/merge | --force option]) with files
53 added on the private branch may only work once, but later abort with
54 "WARNING: no common ancestor for FILE", as the parent-child relationship is
55 not recorded. (See the [/doc/trunk/www/branching.wiki | Branching, Forking,
56 Merging, and Tagging] document for more information.)
57 </div>
58
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -254,12 +254,12 @@
254254
255255
To see the most recent changes made to the repository by other users, use "fossil timeline" to
256256
find out the most recent commit, and then "fossil diff" between that commit and the
257257
current tree:
258258
259
-<pre><b>fossil timeline
260
-=== 2021-03-28 ===
259
+<pre><b><verbatim>fossil timeline
260
+=== 2021-03-28 ===
261261
03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk)
262262
=== 2021-03-27 ===
263263
23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk)
264264
265265
@@ -269,11 +269,11 @@
269269
--- frobnicate.c
270270
+++ frobnicate.c
271271
@@ -1,10 +1,11 @@
272272
+/* made a change to the source file */
273273
# Original text
274
-</b></pre>
274
+</verbatim></b></pre>
275275
276276
"current" is an alias for the checkout version, so the command
277277
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
278278
279279
To commit your changes to a local-only repository:
@@ -296,11 +296,11 @@
296296
To commit your changes to a repository that was cloned from a remote
297297
repository, you give the same command, but the results are different.
298298
Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a
299299
single-stage commit that sends all changes committed to the local
300300
repository immediately on to the remote parent repository. This only
301
-works if you have write permission to the remote respository.
301
+works if you have write permission to the remote repository.
302302
303303
<h2 id="naming">Naming of Files, Checkins, and Branches</h2>
304304
305305
Fossil deals with information artifacts. This Quickstart document only deals
306306
with files and collections of files, but be aware there are also tickets, wiki pages and more.
@@ -363,11 +363,11 @@
363363
as an HTTP server, where the repo DB is on the other side of the HTTP
364364
server wall, inaccessible by all means other than Fossil's own
365365
mediation. For this reason, the "localhost bypasses access control"
366366
policy does <i>not</i> apply to these other interfaces. That is a very
367367
good thing, since without this difference in policy, it would be unsafe
368
-to bind a [/help?cmd=server | <b>fossil server</b>] instance to
368
+to bind a [/help/server | <b>fossil server</b>] instance to
369369
localhost on a high-numbered port and then reverse-proxy it out to the
370370
world via HTTPS, a practice this author does engage in, with confidence.)
371371
372372
Once you are finished configuring Fossil, you may safely Control-C out
373373
of the <b>fossil&nbsp;ui</b> command to shut down this privileged
374374
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -254,12 +254,12 @@
254
255 To see the most recent changes made to the repository by other users, use "fossil timeline" to
256 find out the most recent commit, and then "fossil diff" between that commit and the
257 current tree:
258
259 <pre><b>fossil timeline
260 === 2021-03-28 ===
261 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk)
262 === 2021-03-27 ===
263 23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk)
264
265
@@ -269,11 +269,11 @@
269 --- frobnicate.c
270 +++ frobnicate.c
271 @@ -1,10 +1,11 @@
272 +/* made a change to the source file */
273 # Original text
274 </b></pre>
275
276 "current" is an alias for the checkout version, so the command
277 "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
278
279 To commit your changes to a local-only repository:
@@ -296,11 +296,11 @@
296 To commit your changes to a repository that was cloned from a remote
297 repository, you give the same command, but the results are different.
298 Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a
299 single-stage commit that sends all changes committed to the local
300 repository immediately on to the remote parent repository. This only
301 works if you have write permission to the remote respository.
302
303 <h2 id="naming">Naming of Files, Checkins, and Branches</h2>
304
305 Fossil deals with information artifacts. This Quickstart document only deals
306 with files and collections of files, but be aware there are also tickets, wiki pages and more.
@@ -363,11 +363,11 @@
363 as an HTTP server, where the repo DB is on the other side of the HTTP
364 server wall, inaccessible by all means other than Fossil's own
365 mediation. For this reason, the "localhost bypasses access control"
366 policy does <i>not</i> apply to these other interfaces. That is a very
367 good thing, since without this difference in policy, it would be unsafe
368 to bind a [/help?cmd=server | <b>fossil server</b>] instance to
369 localhost on a high-numbered port and then reverse-proxy it out to the
370 world via HTTPS, a practice this author does engage in, with confidence.)
371
372 Once you are finished configuring Fossil, you may safely Control-C out
373 of the <b>fossil&nbsp;ui</b> command to shut down this privileged
374
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -254,12 +254,12 @@
254
255 To see the most recent changes made to the repository by other users, use "fossil timeline" to
256 find out the most recent commit, and then "fossil diff" between that commit and the
257 current tree:
258
259 <pre><b><verbatim>fossil timeline
260 === 2021-03-28 ===
261 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk)
262 === 2021-03-27 ===
263 23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk)
264
265
@@ -269,11 +269,11 @@
269 --- frobnicate.c
270 +++ frobnicate.c
271 @@ -1,10 +1,11 @@
272 +/* made a change to the source file */
273 # Original text
274 </verbatim></b></pre>
275
276 "current" is an alias for the checkout version, so the command
277 "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
278
279 To commit your changes to a local-only repository:
@@ -296,11 +296,11 @@
296 To commit your changes to a repository that was cloned from a remote
297 repository, you give the same command, but the results are different.
298 Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a
299 single-stage commit that sends all changes committed to the local
300 repository immediately on to the remote parent repository. This only
301 works if you have write permission to the remote repository.
302
303 <h2 id="naming">Naming of Files, Checkins, and Branches</h2>
304
305 Fossil deals with information artifacts. This Quickstart document only deals
306 with files and collections of files, but be aware there are also tickets, wiki pages and more.
@@ -363,11 +363,11 @@
363 as an HTTP server, where the repo DB is on the other side of the HTTP
364 server wall, inaccessible by all means other than Fossil's own
365 mediation. For this reason, the "localhost bypasses access control"
366 policy does <i>not</i> apply to these other interfaces. That is a very
367 good thing, since without this difference in policy, it would be unsafe
368 to bind a [/help/server | <b>fossil server</b>] instance to
369 localhost on a high-numbered port and then reverse-proxy it out to the
370 world via HTTPS, a practice this author does engage in, with confidence.)
371
372 Once you are finished configuring Fossil, you may safely Control-C out
373 of the <b>fossil&nbsp;ui</b> command to shut down this privileged
374
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -373,11 +373,11 @@
373373
developers to be smarter than the original developers! That's a
374374
beautiful wish, but there's a sharp limit to how far you can carry it.
375375
Eventually you hit the limits of human brilliance.
376376
377377
When the operation of some bit of code is not obvious, both Fossil and
378
-Git let you run a [`blame`](/help?cmd=blame) on the code file to get
378
+Git let you run a [`blame`](/help/blame) on the code file to get
379379
information about each line of code, and from that which check-in last
380380
touched a given line of code. If you squash the check-ins on a branch
381381
down to a single check-in, you throw away the information leading up to
382382
that finished form. Fossil not only preserves the check-ins surrounding
383383
the one that included the line of code you're trying to understand, its
384384
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -373,11 +373,11 @@
373 developers to be smarter than the original developers! That's a
374 beautiful wish, but there's a sharp limit to how far you can carry it.
375 Eventually you hit the limits of human brilliance.
376
377 When the operation of some bit of code is not obvious, both Fossil and
378 Git let you run a [`blame`](/help?cmd=blame) on the code file to get
379 information about each line of code, and from that which check-in last
380 touched a given line of code. If you squash the check-ins on a branch
381 down to a single check-in, you throw away the information leading up to
382 that finished form. Fossil not only preserves the check-ins surrounding
383 the one that included the line of code you're trying to understand, its
384
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -373,11 +373,11 @@
373 developers to be smarter than the original developers! That's a
374 beautiful wish, but there's a sharp limit to how far you can carry it.
375 Eventually you hit the limits of human brilliance.
376
377 When the operation of some bit of code is not obvious, both Fossil and
378 Git let you run a [`blame`](/help/blame) on the code file to get
379 information about each line of code, and from that which check-in last
380 touched a given line of code. If you squash the check-ins on a branch
381 down to a single check-in, you throw away the information leading up to
382 that finished form. Fossil not only preserves the check-ins surrounding
383 the one that included the line of code you're trying to understand, its
384
--- www/relatedwork.md
+++ www/relatedwork.md
@@ -66,11 +66,11 @@
6666
[corec66]: https://corecursive.com/066-sqlite-with-richard-hipp/
6767
[Darcs]: http://darcs.net/
6868
[db2w]: https://youtu.be/2eaQzahCeh4
6969
[emacsfsl]: https://chiselapp.com/user/venks/repository/emacs-fossil/doc/tip/README.md
7070
[floss26]: https://twit.tv/shows/floss-weekly/episodes/26
71
-[fnc]: https://fnc.bsdbox.org
71
+[fnc]: https://fnc.sh
7272
[fsl]: http://fossil.0branch.com/fsl
7373
[Fuel]: https://fuel-scm.org/fossil/index
7474
[Git]: https://git-scm.com
7575
[GoLand]: https://www.jetbrains.com/go/
7676
[got]: https://gameoftrees.org
7777
--- www/relatedwork.md
+++ www/relatedwork.md
@@ -66,11 +66,11 @@
66 [corec66]: https://corecursive.com/066-sqlite-with-richard-hipp/
67 [Darcs]: http://darcs.net/
68 [db2w]: https://youtu.be/2eaQzahCeh4
69 [emacsfsl]: https://chiselapp.com/user/venks/repository/emacs-fossil/doc/tip/README.md
70 [floss26]: https://twit.tv/shows/floss-weekly/episodes/26
71 [fnc]: https://fnc.bsdbox.org
72 [fsl]: http://fossil.0branch.com/fsl
73 [Fuel]: https://fuel-scm.org/fossil/index
74 [Git]: https://git-scm.com
75 [GoLand]: https://www.jetbrains.com/go/
76 [got]: https://gameoftrees.org
77
--- www/relatedwork.md
+++ www/relatedwork.md
@@ -66,11 +66,11 @@
66 [corec66]: https://corecursive.com/066-sqlite-with-richard-hipp/
67 [Darcs]: http://darcs.net/
68 [db2w]: https://youtu.be/2eaQzahCeh4
69 [emacsfsl]: https://chiselapp.com/user/venks/repository/emacs-fossil/doc/tip/README.md
70 [floss26]: https://twit.tv/shows/floss-weekly/episodes/26
71 [fnc]: https://fnc.sh
72 [fsl]: http://fossil.0branch.com/fsl
73 [Fuel]: https://fuel-scm.org/fossil/index
74 [Git]: https://git-scm.com
75 [GoLand]: https://www.jetbrains.com/go/
76 [got]: https://gameoftrees.org
77
--- www/selfhost.wiki
+++ www/selfhost.wiki
@@ -72,7 +72,7 @@
7272
7373
Server (2) is a
7474
<a href="http://www.linode.com/">Linode</a> located in Newark, NJ
7575
and set up just like the canonical server (1) with the addition of a
7676
cron job for synchronization. The same cron job also runs the
77
-[/help?cmd=git|fossil git export] command after each sync in order to
77
+[/help/git|fossil git export] command after each sync in order to
7878
[./mirrortogithub.md#ex1|mirror all changes to GitHub].
7979
--- www/selfhost.wiki
+++ www/selfhost.wiki
@@ -72,7 +72,7 @@
72
73 Server (2) is a
74 <a href="http://www.linode.com/">Linode</a> located in Newark, NJ
75 and set up just like the canonical server (1) with the addition of a
76 cron job for synchronization. The same cron job also runs the
77 [/help?cmd=git|fossil git export] command after each sync in order to
78 [./mirrortogithub.md#ex1|mirror all changes to GitHub].
79
--- www/selfhost.wiki
+++ www/selfhost.wiki
@@ -72,7 +72,7 @@
72
73 Server (2) is a
74 <a href="http://www.linode.com/">Linode</a> located in Newark, NJ
75 and set up just like the canonical server (1) with the addition of a
76 cron job for synchronization. The same cron job also runs the
77 [/help/git|fossil git export] command after each sync in order to
78 [./mirrortogithub.md#ex1|mirror all changes to GitHub].
79
--- www/server/index.html
+++ www/server/index.html
@@ -38,16 +38,16 @@
3838
3939
4040
<h2 id="prep">Repository Prep</h2>
4141
4242
<p>Prior to serving a Fossil repository to others, consider running <a
43
-href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these
43
+href="$ROOT/help/ui"><tt>fossil ui</tt></a> locally and taking these
4444
minimum recommended preparation steps:</p>
4545
4646
<ol>
4747
<li><p>Fossil creates only one user in a <a
48
- href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
48
+ href="$ROOT/help/new">new repository</a> and gives it the <a
4949
href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
5050
The 10-digit random password generated for that user is fairly strong
5151
against remote attack, even without explicit password guess rate
5252
limiting, but because that user has so much power, you may want to
5353
give it a much stronger password under Admin → Users.</a></li>
@@ -95,11 +95,11 @@
9595
9696
<p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
9797
CGI script</a>. This method is known to work with Apache,
9898
<tt>lighttpd</tt>, and <a
9999
href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
100
-administrator places a <a href="$ROOT/help?cmd=cgi">short CGI script</a> in
100
+administrator places a <a href="$ROOT/help/cgi">short CGI script</a> in
101101
the web server's document hierarchy and when a client requests the URL
102102
that corresponds to that script, Fossil runs and generates the
103103
response.</p>
104104
105105
<p>CGI is a good choice for merging Fossil into an existing web site,
@@ -114,11 +114,11 @@
114114
href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
115115
href="any/stunnel.md"><tt>stunnel</tt></a>, <a
116116
href="macos/service.md"><tt>launchd</tt></a>, and <a
117117
href="debian/service.md"><tt>systemd</tt></a>
118118
can be configured to invoke the the
119
-<a href="$ROOT/help?cmd=http"><tt>fossil http</tt></a> command to handle
119
+<a href="$ROOT/help/http"><tt>fossil http</tt></a> command to handle
120120
each incoming HTTP request. The "<tt>fossil http</tt>" command reads
121121
the HTTP request off of standard input, computes an appropriate
122122
reply, and writes the reply on standard output. There is a separate
123123
invocation of the "<tt>fossil http</tt>" command for each HTTP request.
124124
The socket listener daemon takes care of relaying content to and from
@@ -127,21 +127,21 @@
127127
128128
<h3 id="standalone">Stand-alone HTTP Server</h3>
129129
130130
<p>This is the <a href="any/none.md">easiest method</a>.
131131
A stand-alone server uses the
132
-<a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command to run a
132
+<a href="$ROOT/help/server"><tt>fossil server</tt></a> command to run a
133133
process that listens for incoming HTTP requests on a socket and then
134134
dispatches a copy of itself to deal with each incoming request. You can
135135
expose Fossil directly to the clients in this way or you can interpose a
136136
<a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
137137
layer between the clients and Fossil.</p>
138138
139139
<h3 id="scgi">SCGI</h3>
140140
141141
<p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
142
-When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is
142
+When the <a href="$ROOT/help/server"><tt>fossil server</tt></a> command is
143143
run with the extra <tt>--scgi</tt> option, it listens for incoming
144144
SCGI requests rather than HTTP requests. This allows Fossil to
145145
respond to requests from web servers <a href="debian/nginx.md">such as
146146
nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
147147
than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
@@ -287,11 +287,11 @@
287287
activating the search feature (Admin → Search) so that visitors can do
288288
full-text search on your documentation.</p></li>
289289
290290
<li><p>Now that others can be making changes to the repository,
291291
consider monitoring them via <a href="../alerts.md">email alerts</a>
292
- or the <a href="$ROOT/help?cmd=/timeline.rss">timeline RSS
292
+ or the <a href="$ROOT/help/www/timeline.rss">timeline RSS
293293
feed</a>.</p></li>
294294
295295
<li><p>Turn on the various logging features.</p></li>
296296
</ol>
297297
298298
--- www/server/index.html
+++ www/server/index.html
@@ -38,16 +38,16 @@
38
39
40 <h2 id="prep">Repository Prep</h2>
41
42 <p>Prior to serving a Fossil repository to others, consider running <a
43 href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these
44 minimum recommended preparation steps:</p>
45
46 <ol>
47 <li><p>Fossil creates only one user in a <a
48 href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
49 href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
50 The 10-digit random password generated for that user is fairly strong
51 against remote attack, even without explicit password guess rate
52 limiting, but because that user has so much power, you may want to
53 give it a much stronger password under Admin → Users.</a></li>
@@ -95,11 +95,11 @@
95
96 <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
97 CGI script</a>. This method is known to work with Apache,
98 <tt>lighttpd</tt>, and <a
99 href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
100 administrator places a <a href="$ROOT/help?cmd=cgi">short CGI script</a> in
101 the web server's document hierarchy and when a client requests the URL
102 that corresponds to that script, Fossil runs and generates the
103 response.</p>
104
105 <p>CGI is a good choice for merging Fossil into an existing web site,
@@ -114,11 +114,11 @@
114 href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
115 href="any/stunnel.md"><tt>stunnel</tt></a>, <a
116 href="macos/service.md"><tt>launchd</tt></a>, and <a
117 href="debian/service.md"><tt>systemd</tt></a>
118 can be configured to invoke the the
119 <a href="$ROOT/help?cmd=http"><tt>fossil http</tt></a> command to handle
120 each incoming HTTP request. The "<tt>fossil http</tt>" command reads
121 the HTTP request off of standard input, computes an appropriate
122 reply, and writes the reply on standard output. There is a separate
123 invocation of the "<tt>fossil http</tt>" command for each HTTP request.
124 The socket listener daemon takes care of relaying content to and from
@@ -127,21 +127,21 @@
127
128 <h3 id="standalone">Stand-alone HTTP Server</h3>
129
130 <p>This is the <a href="any/none.md">easiest method</a>.
131 A stand-alone server uses the
132 <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command to run a
133 process that listens for incoming HTTP requests on a socket and then
134 dispatches a copy of itself to deal with each incoming request. You can
135 expose Fossil directly to the clients in this way or you can interpose a
136 <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
137 layer between the clients and Fossil.</p>
138
139 <h3 id="scgi">SCGI</h3>
140
141 <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
142 When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is
143 run with the extra <tt>--scgi</tt> option, it listens for incoming
144 SCGI requests rather than HTTP requests. This allows Fossil to
145 respond to requests from web servers <a href="debian/nginx.md">such as
146 nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
147 than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
@@ -287,11 +287,11 @@
287 activating the search feature (Admin → Search) so that visitors can do
288 full-text search on your documentation.</p></li>
289
290 <li><p>Now that others can be making changes to the repository,
291 consider monitoring them via <a href="../alerts.md">email alerts</a>
292 or the <a href="$ROOT/help?cmd=/timeline.rss">timeline RSS
293 feed</a>.</p></li>
294
295 <li><p>Turn on the various logging features.</p></li>
296 </ol>
297
298
--- www/server/index.html
+++ www/server/index.html
@@ -38,16 +38,16 @@
38
39
40 <h2 id="prep">Repository Prep</h2>
41
42 <p>Prior to serving a Fossil repository to others, consider running <a
43 href="$ROOT/help/ui"><tt>fossil ui</tt></a> locally and taking these
44 minimum recommended preparation steps:</p>
45
46 <ol>
47 <li><p>Fossil creates only one user in a <a
48 href="$ROOT/help/new">new repository</a> and gives it the <a
49 href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
50 The 10-digit random password generated for that user is fairly strong
51 against remote attack, even without explicit password guess rate
52 limiting, but because that user has so much power, you may want to
53 give it a much stronger password under Admin → Users.</a></li>
@@ -95,11 +95,11 @@
95
96 <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
97 CGI script</a>. This method is known to work with Apache,
98 <tt>lighttpd</tt>, and <a
99 href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
100 administrator places a <a href="$ROOT/help/cgi">short CGI script</a> in
101 the web server's document hierarchy and when a client requests the URL
102 that corresponds to that script, Fossil runs and generates the
103 response.</p>
104
105 <p>CGI is a good choice for merging Fossil into an existing web site,
@@ -114,11 +114,11 @@
114 href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
115 href="any/stunnel.md"><tt>stunnel</tt></a>, <a
116 href="macos/service.md"><tt>launchd</tt></a>, and <a
117 href="debian/service.md"><tt>systemd</tt></a>
118 can be configured to invoke the the
119 <a href="$ROOT/help/http"><tt>fossil http</tt></a> command to handle
120 each incoming HTTP request. The "<tt>fossil http</tt>" command reads
121 the HTTP request off of standard input, computes an appropriate
122 reply, and writes the reply on standard output. There is a separate
123 invocation of the "<tt>fossil http</tt>" command for each HTTP request.
124 The socket listener daemon takes care of relaying content to and from
@@ -127,21 +127,21 @@
127
128 <h3 id="standalone">Stand-alone HTTP Server</h3>
129
130 <p>This is the <a href="any/none.md">easiest method</a>.
131 A stand-alone server uses the
132 <a href="$ROOT/help/server"><tt>fossil server</tt></a> command to run a
133 process that listens for incoming HTTP requests on a socket and then
134 dispatches a copy of itself to deal with each incoming request. You can
135 expose Fossil directly to the clients in this way or you can interpose a
136 <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
137 layer between the clients and Fossil.</p>
138
139 <h3 id="scgi">SCGI</h3>
140
141 <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
142 When the <a href="$ROOT/help/server"><tt>fossil server</tt></a> command is
143 run with the extra <tt>--scgi</tt> option, it listens for incoming
144 SCGI requests rather than HTTP requests. This allows Fossil to
145 respond to requests from web servers <a href="debian/nginx.md">such as
146 nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
147 than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
@@ -287,11 +287,11 @@
287 activating the search feature (Admin → Search) so that visitors can do
288 full-text search on your documentation.</p></li>
289
290 <li><p>Now that others can be making changes to the repository,
291 consider monitoring them via <a href="../alerts.md">email alerts</a>
292 or the <a href="$ROOT/help/www/timeline.rss">timeline RSS
293 feed</a>.</p></li>
294
295 <li><p>Turn on the various logging features.</p></li>
296 </ol>
297
298
--- www/server/windows/service.md
+++ www/server/windows/service.md
@@ -62,11 +62,11 @@
6262
fossil.exe process is unable to use the directory normally during a scan.
6363
6464
### <a id='PowerShell'></a>Advanced service installation using PowerShell
6565
6666
As great as `fossil winsrv` is, it does not have one to one reflection of all of
67
-the `fossil server` [options](/help?cmd=server). When you need to use some of
67
+the `fossil server` [options](/help/server). When you need to use some of
6868
the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will
6969
need to use PowerShell to configure and install the Windows service.
7070
7171
PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1)
7272
command, which we can use to install and configure Fossil as a service. The
@@ -81,11 +81,11 @@
8181
Windows will accept either back slashes or forward slashes in path names, but
8282
Fossil has a preference for forward slashes. The use of `--repolist` will make
8383
this a multiple repository server. If you want to serve only a single
8484
repository, then leave off the `--repolist` parameter and provide the full path
8585
to the proper repository file. Other options are listed in the
86
-[fossil server](/help?cmd=server) documentation.
86
+[fossil server](/help/server) documentation.
8787
8888
The service will be installed by default to use the Local Service account.
8989
Since Fossil only needs access to local files, this is fine and causes no
9090
issues. The service will not be running once installed. You will need to start
9191
it to proceed (the `-StartupType Automatic` parameter to `New-Service` will
9292
--- www/server/windows/service.md
+++ www/server/windows/service.md
@@ -62,11 +62,11 @@
62 fossil.exe process is unable to use the directory normally during a scan.
63
64 ### <a id='PowerShell'></a>Advanced service installation using PowerShell
65
66 As great as `fossil winsrv` is, it does not have one to one reflection of all of
67 the `fossil server` [options](/help?cmd=server). When you need to use some of
68 the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will
69 need to use PowerShell to configure and install the Windows service.
70
71 PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1)
72 command, which we can use to install and configure Fossil as a service. The
@@ -81,11 +81,11 @@
81 Windows will accept either back slashes or forward slashes in path names, but
82 Fossil has a preference for forward slashes. The use of `--repolist` will make
83 this a multiple repository server. If you want to serve only a single
84 repository, then leave off the `--repolist` parameter and provide the full path
85 to the proper repository file. Other options are listed in the
86 [fossil server](/help?cmd=server) documentation.
87
88 The service will be installed by default to use the Local Service account.
89 Since Fossil only needs access to local files, this is fine and causes no
90 issues. The service will not be running once installed. You will need to start
91 it to proceed (the `-StartupType Automatic` parameter to `New-Service` will
92
--- www/server/windows/service.md
+++ www/server/windows/service.md
@@ -62,11 +62,11 @@
62 fossil.exe process is unable to use the directory normally during a scan.
63
64 ### <a id='PowerShell'></a>Advanced service installation using PowerShell
65
66 As great as `fossil winsrv` is, it does not have one to one reflection of all of
67 the `fossil server` [options](/help/server). When you need to use some of
68 the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will
69 need to use PowerShell to configure and install the Windows service.
70
71 PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1)
72 command, which we can use to install and configure Fossil as a service. The
@@ -81,11 +81,11 @@
81 Windows will accept either back slashes or forward slashes in path names, but
82 Fossil has a preference for forward slashes. The use of `--repolist` will make
83 this a multiple repository server. If you want to serve only a single
84 repository, then leave off the `--repolist` parameter and provide the full path
85 to the proper repository file. Other options are listed in the
86 [fossil server](/help/server) documentation.
87
88 The service will be installed by default to use the Local Service account.
89 Since Fossil only needs access to local files, this is fine and causes no
90 issues. The service will not be running once installed. You will need to start
91 it to proceed (the `-StartupType Automatic` parameter to `New-Service` will
92
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -49,11 +49,11 @@
4949
<pre>
5050
https://example-project.org/ext/<i>FILENAME</i>
5151
</pre>
5252
5353
In other words, access files in DOCUMENT_ROOT by appending the filename
54
-relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext]
54
+relative to DOCUMENT_ROOT to the [/help/www/ext|/ext]
5555
page of the Fossil server.
5656
5757
* Files that are readable but not executable are returned as static
5858
content.
5959
@@ -124,11 +124,11 @@
124124
of its own source-code, so you can see how it works.
125125
126126
<h3>2.3 Example #3</h3>
127127
128128
For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME"
129
-option to the [/help?cmd=ui|fossil ui] command is a short cut that treats
129
+option to the [/help/ui|fossil ui] command is a short cut that treats
130130
FILENAME as a CGI extension. When the ui command starts up a new web browser
131131
pages, it points that page to the FILENAME extension. So if FILENAME is
132132
a static content file (such as an HTML file or
133133
[/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the
134134
rendered content of the file is displayed. Meanwhile, the user can be
135135
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -49,11 +49,11 @@
49 <pre>
50 https://example-project.org/ext/<i>FILENAME</i>
51 </pre>
52
53 In other words, access files in DOCUMENT_ROOT by appending the filename
54 relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext]
55 page of the Fossil server.
56
57 * Files that are readable but not executable are returned as static
58 content.
59
@@ -124,11 +124,11 @@
124 of its own source-code, so you can see how it works.
125
126 <h3>2.3 Example #3</h3>
127
128 For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME"
129 option to the [/help?cmd=ui|fossil ui] command is a short cut that treats
130 FILENAME as a CGI extension. When the ui command starts up a new web browser
131 pages, it points that page to the FILENAME extension. So if FILENAME is
132 a static content file (such as an HTML file or
133 [/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the
134 rendered content of the file is displayed. Meanwhile, the user can be
135
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -49,11 +49,11 @@
49 <pre>
50 https://example-project.org/ext/<i>FILENAME</i>
51 </pre>
52
53 In other words, access files in DOCUMENT_ROOT by appending the filename
54 relative to DOCUMENT_ROOT to the [/help/www/ext|/ext]
55 page of the Fossil server.
56
57 * Files that are readable but not executable are returned as static
58 content.
59
@@ -124,11 +124,11 @@
124 of its own source-code, so you can see how it works.
125
126 <h3>2.3 Example #3</h3>
127
128 For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME"
129 option to the [/help/ui|fossil ui] command is a short cut that treats
130 FILENAME as a CGI extension. When the ui command starts up a new web browser
131 pages, it points that page to the FILENAME extension. So if FILENAME is
132 a static content file (such as an HTML file or
133 [/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the
134 rendered content of the file is displayed. Meanwhile, the user can be
135
--- www/ssl-server.md
+++ www/ssl-server.md
@@ -1,13 +1,13 @@
11
# SSL/TLS Server Mode
22
33
## History
44
55
Fossil has supported [client-side SSL/TLS][0] since [2010][1]. This means
6
-that commands like "[fossil sync](/help?cmd=sync)" could use SSL/TLS when
6
+that commands like "[fossil sync](/help/sync)" could use SSL/TLS when
77
contacting a server. But on the server side, commands like
8
-"[fossil server](/help?cmd=server)" operated in clear-text only. To implement
8
+"[fossil server](/help/server)" operated in clear-text only. To implement
99
an encrypted server, you had to put Fossil behind a web server or reverse
1010
proxy that handled the SSL/TLS decryption/encryption and passed cleartext
1111
down to Fossil.
1212
1313
[0]: ./ssl.wiki
@@ -14,13 +14,13 @@
1414
[1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
1515
1616
Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
1717
Fossil servers are now able to converse directly over TLS. Commands like
1818
19
- * "[fossil server](/help?cmd=server)"
20
- * "[fossil ui](/help?cmd=ui)", and
21
- * "[fossil http](/help?cmd=http)"
19
+ * "[fossil server](/help/server)"
20
+ * "[fossil ui](/help/ui)", and
21
+ * "[fossil http](/help/http)"
2222
2323
may now handle the encryption natively when suitably configured, without
2424
requiring a third-party proxy layer.
2525
2626
## <a id="usage"></a>Usage
@@ -135,11 +135,11 @@
135135
individual components will still be easily accessible.
136136
137137
### <a id="cat"></a>Separate or Concatenated?
138138
139139
Given a single concatenated file that holds both your private key and your
140
-cert, you can hand it off to the "[fossil server](/help?cmd=server)"
140
+cert, you can hand it off to the "[fossil server](/help/server)"
141141
command using the `--cert` option, like this:
142142
143143
fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
144144
145145
The command above is sufficient to run a fully-encrypted web site for
@@ -257,12 +257,12 @@
257257
domain control. ACME’s design precludes replay attacks.
258258
259259
In order for all of this to happen, certbot needs to be able to create
260260
a subdirectory named ".well-known", within a directory you specify,
261261
then populate that subdirectory with a token file of some kind. To support
262
-this, the "[fossil server](/help?cmd=server)" and
263
-"[fossil http](/help?cmd=http)" commands have the --acme option.
262
+this, the "[fossil server](/help/server)" and
263
+"[fossil http](/help/http)" commands have the --acme option.
264264
265265
When specified, Fossil sees a URL where the path
266266
begins with ".well-known", then instead of doing its normal processing, it
267267
looks for a file with that pathname and returns it to the client. If
268268
the "server" or "http" command is referencing a single Fossil repository,
269269
--- www/ssl-server.md
+++ www/ssl-server.md
@@ -1,13 +1,13 @@
1 # SSL/TLS Server Mode
2
3 ## History
4
5 Fossil has supported [client-side SSL/TLS][0] since [2010][1]. This means
6 that commands like "[fossil sync](/help?cmd=sync)" could use SSL/TLS when
7 contacting a server. But on the server side, commands like
8 "[fossil server](/help?cmd=server)" operated in clear-text only. To implement
9 an encrypted server, you had to put Fossil behind a web server or reverse
10 proxy that handled the SSL/TLS decryption/encryption and passed cleartext
11 down to Fossil.
12
13 [0]: ./ssl.wiki
@@ -14,13 +14,13 @@
14 [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
15
16 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
17 Fossil servers are now able to converse directly over TLS. Commands like
18
19 * "[fossil server](/help?cmd=server)"
20 * "[fossil ui](/help?cmd=ui)", and
21 * "[fossil http](/help?cmd=http)"
22
23 may now handle the encryption natively when suitably configured, without
24 requiring a third-party proxy layer.
25
26 ## <a id="usage"></a>Usage
@@ -135,11 +135,11 @@
135 individual components will still be easily accessible.
136
137 ### <a id="cat"></a>Separate or Concatenated?
138
139 Given a single concatenated file that holds both your private key and your
140 cert, you can hand it off to the "[fossil server](/help?cmd=server)"
141 command using the `--cert` option, like this:
142
143 fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
144
145 The command above is sufficient to run a fully-encrypted web site for
@@ -257,12 +257,12 @@
257 domain control. ACME’s design precludes replay attacks.
258
259 In order for all of this to happen, certbot needs to be able to create
260 a subdirectory named ".well-known", within a directory you specify,
261 then populate that subdirectory with a token file of some kind. To support
262 this, the "[fossil server](/help?cmd=server)" and
263 "[fossil http](/help?cmd=http)" commands have the --acme option.
264
265 When specified, Fossil sees a URL where the path
266 begins with ".well-known", then instead of doing its normal processing, it
267 looks for a file with that pathname and returns it to the client. If
268 the "server" or "http" command is referencing a single Fossil repository,
269
--- www/ssl-server.md
+++ www/ssl-server.md
@@ -1,13 +1,13 @@
1 # SSL/TLS Server Mode
2
3 ## History
4
5 Fossil has supported [client-side SSL/TLS][0] since [2010][1]. This means
6 that commands like "[fossil sync](/help/sync)" could use SSL/TLS when
7 contacting a server. But on the server side, commands like
8 "[fossil server](/help/server)" operated in clear-text only. To implement
9 an encrypted server, you had to put Fossil behind a web server or reverse
10 proxy that handled the SSL/TLS decryption/encryption and passed cleartext
11 down to Fossil.
12
13 [0]: ./ssl.wiki
@@ -14,13 +14,13 @@
14 [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
15
16 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
17 Fossil servers are now able to converse directly over TLS. Commands like
18
19 * "[fossil server](/help/server)"
20 * "[fossil ui](/help/ui)", and
21 * "[fossil http](/help/http)"
22
23 may now handle the encryption natively when suitably configured, without
24 requiring a third-party proxy layer.
25
26 ## <a id="usage"></a>Usage
@@ -135,11 +135,11 @@
135 individual components will still be easily accessible.
136
137 ### <a id="cat"></a>Separate or Concatenated?
138
139 Given a single concatenated file that holds both your private key and your
140 cert, you can hand it off to the "[fossil server](/help/server)"
141 command using the `--cert` option, like this:
142
143 fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
144
145 The command above is sufficient to run a fully-encrypted web site for
@@ -257,12 +257,12 @@
257 domain control. ACME’s design precludes replay attacks.
258
259 In order for all of this to happen, certbot needs to be able to create
260 a subdirectory named ".well-known", within a directory you specify,
261 then populate that subdirectory with a token file of some kind. To support
262 this, the "[fossil server](/help/server)" and
263 "[fossil http](/help/http)" commands have the --acme option.
264
265 When specified, Fossil sees a URL where the path
266 begins with ".well-known", then instead of doing its normal processing, it
267 looks for a file with that pathname and returns it to the client. If
268 the "server" or "http" command is referencing a single Fossil repository,
269
+7 -7
--- www/sync.wiki
+++ www/sync.wiki
@@ -24,15 +24,15 @@
2424
2525
Each repository also has local state. The local state determines
2626
the web-page formatting preferences, authorized users, ticket formats,
2727
and similar information that varies from one repository to another.
2828
The local state is not usually transferred during a sync. Except,
29
-some local state is transferred during a [/help?cmd=clone|clone]
29
+some local state is transferred during a [/help/clone|clone]
3030
in order to initialize the local state of the new repository. Also,
3131
an administrator can sync local state using
32
-the [/help?cmd=configuration|config push] and
33
-[/help?cmd=configuration|config pull]
32
+the [/help/configuration|config push] and
33
+[/help/configuration|config pull]
3434
commands.
3535
3636
<h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3>
3737
3838
The "bag of artifacts" data model used by Fossil is apparently an
@@ -56,23 +56,23 @@
5656
The server is listening for incoming HTTP requests. The client
5757
issues one or more HTTP requests and receives replies for each
5858
request.
5959
6060
The server might be running as an independent server
61
-using the [/help?cmd=server|"fossil server" command], or it
61
+using the [/help/server|"fossil server" command], or it
6262
might be launched from inetd or xinetd using the
63
-[/help?cmd=http|"fossil http" command]. Or the server might
63
+[/help/http|"fossil http" command]. Or the server might
6464
be [./server/any/cgi.md|launched from CGI] or from
6565
[./server/any/scgi.md|SCGI].
6666
(See "[./server/|How To Configure A Fossil Server]" for details.)
6767
The specifics of how the server listens
6868
for incoming HTTP requests is immaterial to this protocol.
6969
The important point is that the server is listening for requests and
7070
the client is the issuer of the requests.
7171
72
-A single [/help?cmd=push|push],
73
-[/help?cmd=pull|pull], or [/help?cmd=sync|sync]
72
+A single [/help/push|push],
73
+[/help/pull|pull], or [/help?cmd=sync|sync]
7474
might involve multiple HTTP requests.
7575
The client maintains state between all requests. But on the server
7676
side, each request is independent. The server does not preserve
7777
any information about the client from one request to the next.
7878
7979
--- www/sync.wiki
+++ www/sync.wiki
@@ -24,15 +24,15 @@
24
25 Each repository also has local state. The local state determines
26 the web-page formatting preferences, authorized users, ticket formats,
27 and similar information that varies from one repository to another.
28 The local state is not usually transferred during a sync. Except,
29 some local state is transferred during a [/help?cmd=clone|clone]
30 in order to initialize the local state of the new repository. Also,
31 an administrator can sync local state using
32 the [/help?cmd=configuration|config push] and
33 [/help?cmd=configuration|config pull]
34 commands.
35
36 <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3>
37
38 The "bag of artifacts" data model used by Fossil is apparently an
@@ -56,23 +56,23 @@
56 The server is listening for incoming HTTP requests. The client
57 issues one or more HTTP requests and receives replies for each
58 request.
59
60 The server might be running as an independent server
61 using the [/help?cmd=server|"fossil server" command], or it
62 might be launched from inetd or xinetd using the
63 [/help?cmd=http|"fossil http" command]. Or the server might
64 be [./server/any/cgi.md|launched from CGI] or from
65 [./server/any/scgi.md|SCGI].
66 (See "[./server/|How To Configure A Fossil Server]" for details.)
67 The specifics of how the server listens
68 for incoming HTTP requests is immaterial to this protocol.
69 The important point is that the server is listening for requests and
70 the client is the issuer of the requests.
71
72 A single [/help?cmd=push|push],
73 [/help?cmd=pull|pull], or [/help?cmd=sync|sync]
74 might involve multiple HTTP requests.
75 The client maintains state between all requests. But on the server
76 side, each request is independent. The server does not preserve
77 any information about the client from one request to the next.
78
79
--- www/sync.wiki
+++ www/sync.wiki
@@ -24,15 +24,15 @@
24
25 Each repository also has local state. The local state determines
26 the web-page formatting preferences, authorized users, ticket formats,
27 and similar information that varies from one repository to another.
28 The local state is not usually transferred during a sync. Except,
29 some local state is transferred during a [/help/clone|clone]
30 in order to initialize the local state of the new repository. Also,
31 an administrator can sync local state using
32 the [/help/configuration|config push] and
33 [/help/configuration|config pull]
34 commands.
35
36 <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3>
37
38 The "bag of artifacts" data model used by Fossil is apparently an
@@ -56,23 +56,23 @@
56 The server is listening for incoming HTTP requests. The client
57 issues one or more HTTP requests and receives replies for each
58 request.
59
60 The server might be running as an independent server
61 using the [/help/server|"fossil server" command], or it
62 might be launched from inetd or xinetd using the
63 [/help/http|"fossil http" command]. Or the server might
64 be [./server/any/cgi.md|launched from CGI] or from
65 [./server/any/scgi.md|SCGI].
66 (See "[./server/|How To Configure A Fossil Server]" for details.)
67 The specifics of how the server listens
68 for incoming HTTP requests is immaterial to this protocol.
69 The important point is that the server is listening for requests and
70 the client is the issuer of the requests.
71
72 A single [/help/push|push],
73 [/help/pull|pull], or [/help?cmd=sync|sync]
74 might involve multiple HTTP requests.
75 The client maintains state between all requests. But on the server
76 side, each request is independent. The server does not preserve
77 any information about the client from one request to the next.
78
79
--- www/tech_overview.wiki
+++ www/tech_overview.wiki
@@ -162,11 +162,11 @@
162162
FOSSIL_HOME environment variable can always be set to determine the
163163
location of the configuration database. Note also that the configuration
164164
database file itself is called ".fossil" or "fossil.db" on unix but
165165
"_fossil" on windows.
166166
167
-The [/help?cmd=info|fossil info] command will show the location of
167
+The [/help/info|fossil info] command will show the location of
168168
the configuration database on a line that starts with "config-db:".
169169
170170
<h3>2.2 Repository Databases</h3>
171171
172172
The repository database is the file that is commonly referred to as
173173
--- www/tech_overview.wiki
+++ www/tech_overview.wiki
@@ -162,11 +162,11 @@
162 FOSSIL_HOME environment variable can always be set to determine the
163 location of the configuration database. Note also that the configuration
164 database file itself is called ".fossil" or "fossil.db" on unix but
165 "_fossil" on windows.
166
167 The [/help?cmd=info|fossil info] command will show the location of
168 the configuration database on a line that starts with "config-db:".
169
170 <h3>2.2 Repository Databases</h3>
171
172 The repository database is the file that is commonly referred to as
173
--- www/tech_overview.wiki
+++ www/tech_overview.wiki
@@ -162,11 +162,11 @@
162 FOSSIL_HOME environment variable can always be set to determine the
163 location of the configuration database. Note also that the configuration
164 database file itself is called ".fossil" or "fossil.db" on unix but
165 "_fossil" on windows.
166
167 The [/help/info|fossil info] command will show the location of
168 the configuration database on a line that starts with "config-db:".
169
170 <h3>2.2 Repository Databases</h3>
171
172 The repository database is the file that is commonly referred to as
173
+1 -1
--- www/th1.md
+++ www/th1.md
@@ -131,11 +131,11 @@
131131
derived from user inputs that might contain text that is designed to subvert
132132
the script. Untainted strings are known to come from secure sources and
133133
are assumed to contain no malicious content.
134134
135135
Beginning with Fossil version 2.26, and depending on the value of the
136
-[vuln-report setting](/help?cmd=vuln-report), TH1 will prevent tainted
136
+[vuln-report setting](/help/vuln-report), TH1 will prevent tainted
137137
strings from being used in ways that might lead to XSS or SQL-injection
138138
attacks. This feature helps to ensure that XSS and SQL-injection
139139
vulnerabilities are not *accidentally* added to Fossil when
140140
custom TH1 scripts for headers or footers or tickets are added to a
141141
repository. Note that the tainted/untainted distinction in strings does
142142
--- www/th1.md
+++ www/th1.md
@@ -131,11 +131,11 @@
131 derived from user inputs that might contain text that is designed to subvert
132 the script. Untainted strings are known to come from secure sources and
133 are assumed to contain no malicious content.
134
135 Beginning with Fossil version 2.26, and depending on the value of the
136 [vuln-report setting](/help?cmd=vuln-report), TH1 will prevent tainted
137 strings from being used in ways that might lead to XSS or SQL-injection
138 attacks. This feature helps to ensure that XSS and SQL-injection
139 vulnerabilities are not *accidentally* added to Fossil when
140 custom TH1 scripts for headers or footers or tickets are added to a
141 repository. Note that the tainted/untainted distinction in strings does
142
--- www/th1.md
+++ www/th1.md
@@ -131,11 +131,11 @@
131 derived from user inputs that might contain text that is designed to subvert
132 the script. Untainted strings are known to come from secure sources and
133 are assumed to contain no malicious content.
134
135 Beginning with Fossil version 2.26, and depending on the value of the
136 [vuln-report setting](/help/vuln-report), TH1 will prevent tainted
137 strings from being used in ways that might lead to XSS or SQL-injection
138 attacks. This feature helps to ensure that XSS and SQL-injection
139 vulnerabilities are not *accidentally* added to Fossil when
140 custom TH1 scripts for headers or footers or tickets are added to a
141 repository. Note that the tainted/untainted distinction in strings does
142
+6 -6
--- www/unvers.wiki
+++ www/unvers.wiki
@@ -24,15 +24,15 @@
2424
In other words, the URI method "<b>uv</b>" (short for "unversioned")
2525
followed by the name of the unversioned file will retrieve the content
2626
of the file. The MIME type is inferred from the filename suffix.
2727
2828
The content of unversioned files can also be retrieved using the
29
-[/help?cmd=unversioned|fossil unvers cat <i>FILENAME...</i>]
30
-or [/help?cmd=unversioned|fossil unvers export <i>FILENAME</i>] commands.
29
+[/help/unversioned|fossil unvers cat <i>FILENAME...</i>]
30
+or [/help/unversioned|fossil unvers export <i>FILENAME</i>] commands.
3131
3232
A list of all unversioned files on a server can be seen using
33
-the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example].)
33
+the [/help/www/uvlist|/uvlist] URL. ([/uvlist|example].)
3434
3535
<h2>Syncing Unversioned Files</h2>
3636
3737
Unversioned content does not sync between repositories by default.
3838
One must request it via commands such as:
@@ -41,18 +41,18 @@
4141
fossil sync <b>-u</b>
4242
fossil clone <b>-u</b> <i>URL local-repo-name</i>
4343
fossil unversioned sync
4444
</pre>
4545
46
-The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone]
46
+The [/help/sync|fossil sync] and [/help/clone|fossil clone]
4747
commands will synchronize unversioned content if and only if they're
4848
given the "-u" (or "--unversioned") command-line option. The
49
-[/help?cmd=unversioned|fossil unversioned sync] command synchronizes the
49
+[/help/unversioned|fossil unversioned sync] command synchronizes the
5050
unversioned content without synchronizing anything else.
5151
5252
Notice that the "-u" option does not work on
53
-[/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull].
53
+[/help/push|fossil push] or [/help?cmd=pull|fossil pull].
5454
The "-u" option is only available on "sync" and "clone".
5555
A rough equivalent of an unversioned pull would be the
5656
[/help?cmd=unversioned|fossil unversioned revert] command. The
5757
"unversioned revert"
5858
command causes the unversioned content on the local repository to be
5959
--- www/unvers.wiki
+++ www/unvers.wiki
@@ -24,15 +24,15 @@
24 In other words, the URI method "<b>uv</b>" (short for "unversioned")
25 followed by the name of the unversioned file will retrieve the content
26 of the file. The MIME type is inferred from the filename suffix.
27
28 The content of unversioned files can also be retrieved using the
29 [/help?cmd=unversioned|fossil unvers cat <i>FILENAME...</i>]
30 or [/help?cmd=unversioned|fossil unvers export <i>FILENAME</i>] commands.
31
32 A list of all unversioned files on a server can be seen using
33 the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example].)
34
35 <h2>Syncing Unversioned Files</h2>
36
37 Unversioned content does not sync between repositories by default.
38 One must request it via commands such as:
@@ -41,18 +41,18 @@
41 fossil sync <b>-u</b>
42 fossil clone <b>-u</b> <i>URL local-repo-name</i>
43 fossil unversioned sync
44 </pre>
45
46 The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone]
47 commands will synchronize unversioned content if and only if they're
48 given the "-u" (or "--unversioned") command-line option. The
49 [/help?cmd=unversioned|fossil unversioned sync] command synchronizes the
50 unversioned content without synchronizing anything else.
51
52 Notice that the "-u" option does not work on
53 [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull].
54 The "-u" option is only available on "sync" and "clone".
55 A rough equivalent of an unversioned pull would be the
56 [/help?cmd=unversioned|fossil unversioned revert] command. The
57 "unversioned revert"
58 command causes the unversioned content on the local repository to be
59
--- www/unvers.wiki
+++ www/unvers.wiki
@@ -24,15 +24,15 @@
24 In other words, the URI method "<b>uv</b>" (short for "unversioned")
25 followed by the name of the unversioned file will retrieve the content
26 of the file. The MIME type is inferred from the filename suffix.
27
28 The content of unversioned files can also be retrieved using the
29 [/help/unversioned|fossil unvers cat <i>FILENAME...</i>]
30 or [/help/unversioned|fossil unvers export <i>FILENAME</i>] commands.
31
32 A list of all unversioned files on a server can be seen using
33 the [/help/www/uvlist|/uvlist] URL. ([/uvlist|example].)
34
35 <h2>Syncing Unversioned Files</h2>
36
37 Unversioned content does not sync between repositories by default.
38 One must request it via commands such as:
@@ -41,18 +41,18 @@
41 fossil sync <b>-u</b>
42 fossil clone <b>-u</b> <i>URL local-repo-name</i>
43 fossil unversioned sync
44 </pre>
45
46 The [/help/sync|fossil sync] and [/help/clone|fossil clone]
47 commands will synchronize unversioned content if and only if they're
48 given the "-u" (or "--unversioned") command-line option. The
49 [/help/unversioned|fossil unversioned sync] command synchronizes the
50 unversioned content without synchronizing anything else.
51
52 Notice that the "-u" option does not work on
53 [/help/push|fossil push] or [/help?cmd=pull|fossil pull].
54 The "-u" option is only available on "sync" and "clone".
55 A rough equivalent of an unversioned pull would be the
56 [/help?cmd=unversioned|fossil unversioned revert] command. The
57 "unversioned revert"
58 command causes the unversioned content on the local repository to be
59

Keyboard Shortcuts

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