Fossil SCM

Merge from trunk

george 2020-12-14 20:34 wiki-history merge
Commit df330b709f28be40469972a64ea0bf5c65fa0efe6a847f7dc9096986f2efed86
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -27,15 +27,15 @@
2727
if {[hascap oh]} {
2828
if {![info exists current_checkin]} {set current_checkin tip}
2929
menulink /dir?ci=$current_checkin Files desktoponly
3030
}
3131
if {[hascap o]} {
32
- menulink /brlist Branches desktoponly
32
+ menulink /brlist Branches wideonly
3333
menulink /taglist Tags wideonly
3434
}
3535
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
36
- menulink /forum Forum wideonly
36
+ menulink /forum Forum desktoponly
3737
}
3838
if {[hascap r]} {
3939
menulink /ticket Tickets wideonly
4040
}
4141
if {[hascap j]} {
4242
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -27,15 +27,15 @@
27 if {[hascap oh]} {
28 if {![info exists current_checkin]} {set current_checkin tip}
29 menulink /dir?ci=$current_checkin Files desktoponly
30 }
31 if {[hascap o]} {
32 menulink /brlist Branches desktoponly
33 menulink /taglist Tags wideonly
34 }
35 if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
36 menulink /forum Forum wideonly
37 }
38 if {[hascap r]} {
39 menulink /ticket Tickets wideonly
40 }
41 if {[hascap j]} {
42
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -27,15 +27,15 @@
27 if {[hascap oh]} {
28 if {![info exists current_checkin]} {set current_checkin tip}
29 menulink /dir?ci=$current_checkin Files desktoponly
30 }
31 if {[hascap o]} {
32 menulink /brlist Branches wideonly
33 menulink /taglist Tags wideonly
34 }
35 if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
36 menulink /forum Forum desktoponly
37 }
38 if {[hascap r]} {
39 menulink /ticket Tickets wideonly
40 }
41 if {[hascap j]} {
42
+17 -1
--- src/allrepo.c
+++ src/allrepo.c
@@ -91,10 +91,13 @@
9191
** line options supported by the extra command itself, if any
9292
** are present, are passed along verbatim.
9393
**
9494
** fts-config Run the "fts-config" command on all repositories.
9595
**
96
+** git export Do the "git export" command on all repositories for which
97
+** a Git mirror has been previously established.
98
+**
9699
** info Run the "info" command on all repositories.
97100
**
98101
** pull Run a "pull" operation on all repositories. Only the
99102
** --verbose option is supported.
100103
**
@@ -236,10 +239,23 @@
236239
collect_argument_value(&extra, "ignore");
237240
collect_argument(&extra, "rel-paths",0);
238241
useCheckouts = 1;
239242
stopOnError = 0;
240243
quiet = 1;
244
+ }else if( strncmp(zCmd, "git", n)==0 ){
245
+ if( g.argc<4 ){
246
+ usage("git (export|status)");
247
+ }else{
248
+ int n3 = (int)strlen(g.argv[3]);
249
+ if( strncmp(g.argv[3], "export", n3)==0 ){
250
+ zCmd = "git export --if-mirrored -R";
251
+ }else if( strncmp(g.argv[3], "status", n3)==0 ){
252
+ zCmd = "git status -R";
253
+ }else{
254
+ usage("git (export|status)");
255
+ }
256
+ }
241257
}else if( strncmp(zCmd, "push", n)==0 ){
242258
zCmd = "push -autourl -R";
243259
collect_argument(&extra, "verbose","v");
244260
}else if( strncmp(zCmd, "pull", n)==0 ){
245261
zCmd = "pull -autourl -R";
@@ -358,11 +374,11 @@
358374
zCmd = "cache -R";
359375
showLabel = 1;
360376
collect_argv(&extra, 3);
361377
}else{
362378
fossil_fatal("\"all\" subcommand should be one of: "
363
- "add cache changes clean dbstat extras fts-config ignore "
379
+ "add cache changes clean dbstat extras fts-config git ignore "
364380
"info list ls pull push rebuild server setting sync ui unset");
365381
}
366382
verify_all_options();
367383
db_multi_exec("CREATE TEMP TABLE repolist(name,tag);");
368384
if( useCheckouts ){
369385
--- src/allrepo.c
+++ src/allrepo.c
@@ -91,10 +91,13 @@
91 ** line options supported by the extra command itself, if any
92 ** are present, are passed along verbatim.
93 **
94 ** fts-config Run the "fts-config" command on all repositories.
95 **
 
 
 
96 ** info Run the "info" command on all repositories.
97 **
98 ** pull Run a "pull" operation on all repositories. Only the
99 ** --verbose option is supported.
100 **
@@ -236,10 +239,23 @@
236 collect_argument_value(&extra, "ignore");
237 collect_argument(&extra, "rel-paths",0);
238 useCheckouts = 1;
239 stopOnError = 0;
240 quiet = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
241 }else if( strncmp(zCmd, "push", n)==0 ){
242 zCmd = "push -autourl -R";
243 collect_argument(&extra, "verbose","v");
244 }else if( strncmp(zCmd, "pull", n)==0 ){
245 zCmd = "pull -autourl -R";
@@ -358,11 +374,11 @@
358 zCmd = "cache -R";
359 showLabel = 1;
360 collect_argv(&extra, 3);
361 }else{
362 fossil_fatal("\"all\" subcommand should be one of: "
363 "add cache changes clean dbstat extras fts-config ignore "
364 "info list ls pull push rebuild server setting sync ui unset");
365 }
366 verify_all_options();
367 db_multi_exec("CREATE TEMP TABLE repolist(name,tag);");
368 if( useCheckouts ){
369
--- src/allrepo.c
+++ src/allrepo.c
@@ -91,10 +91,13 @@
91 ** line options supported by the extra command itself, if any
92 ** are present, are passed along verbatim.
93 **
94 ** fts-config Run the "fts-config" command on all repositories.
95 **
96 ** git export Do the "git export" command on all repositories for which
97 ** a Git mirror has been previously established.
98 **
99 ** info Run the "info" command on all repositories.
100 **
101 ** pull Run a "pull" operation on all repositories. Only the
102 ** --verbose option is supported.
103 **
@@ -236,10 +239,23 @@
239 collect_argument_value(&extra, "ignore");
240 collect_argument(&extra, "rel-paths",0);
241 useCheckouts = 1;
242 stopOnError = 0;
243 quiet = 1;
244 }else if( strncmp(zCmd, "git", n)==0 ){
245 if( g.argc<4 ){
246 usage("git (export|status)");
247 }else{
248 int n3 = (int)strlen(g.argv[3]);
249 if( strncmp(g.argv[3], "export", n3)==0 ){
250 zCmd = "git export --if-mirrored -R";
251 }else if( strncmp(g.argv[3], "status", n3)==0 ){
252 zCmd = "git status -R";
253 }else{
254 usage("git (export|status)");
255 }
256 }
257 }else if( strncmp(zCmd, "push", n)==0 ){
258 zCmd = "push -autourl -R";
259 collect_argument(&extra, "verbose","v");
260 }else if( strncmp(zCmd, "pull", n)==0 ){
261 zCmd = "pull -autourl -R";
@@ -358,11 +374,11 @@
374 zCmd = "cache -R";
375 showLabel = 1;
376 collect_argv(&extra, 3);
377 }else{
378 fossil_fatal("\"all\" subcommand should be one of: "
379 "add cache changes clean dbstat extras fts-config git ignore "
380 "info list ls pull push rebuild server setting sync ui unset");
381 }
382 verify_all_options();
383 db_multi_exec("CREATE TEMP TABLE repolist(name,tag);");
384 if( useCheckouts ){
385
+1 -1
--- src/backlink.c
+++ src/backlink.c
@@ -130,11 +130,11 @@
130130
int srcid = db_column_int(&q, 2);
131131
const char *zMtime = db_column_text(&q, 3);
132132
@ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a>
133133
switch( srctype ){
134134
case BKLNK_COMMENT: {
135
- @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a>
135
+ @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a>
136136
break;
137137
}
138138
case BKLNK_TICKET: {
139139
@ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a>
140140
break;
141141
--- src/backlink.c
+++ src/backlink.c
@@ -130,11 +130,11 @@
130 int srcid = db_column_int(&q, 2);
131 const char *zMtime = db_column_text(&q, 3);
132 @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a>
133 switch( srctype ){
134 case BKLNK_COMMENT: {
135 @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a>
136 break;
137 }
138 case BKLNK_TICKET: {
139 @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a>
140 break;
141
--- src/backlink.c
+++ src/backlink.c
@@ -130,11 +130,11 @@
130 int srcid = db_column_int(&q, 2);
131 const char *zMtime = db_column_text(&q, 3);
132 @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a>
133 switch( srctype ){
134 case BKLNK_COMMENT: {
135 @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a>
136 break;
137 }
138 case BKLNK_TICKET: {
139 @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a>
140 break;
141
+3 -3
--- src/builtin.c
+++ src/builtin.c
@@ -843,18 +843,18 @@
843843
** builtin_request_js()) with any other app-/page-specific JS it may
844844
** need.
845845
**
846846
** Example usage:
847847
**
848
-** builtin_fossil_js_bundle_or("dom", "fetch", 0);
848
+** builtin_fossil_js_bundle_or("dom", "fetch", NULL);
849849
**
850850
** In bundled mode, that will (the first time it is called) emit all
851851
** builtin fossil JS APIs and "fulfill" the queue immediately. In
852852
** non-bundled mode it will queue up the "dom" and "fetch" APIs to be
853853
** emitted the next time builtin_fulfill_js_requests() is called.
854854
*/
855
-void builtin_fossil_js_bundle_or( const char * zApi, ... ) {
855
+NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) {
856856
static int bundled = 0;
857857
const char *zArg;
858858
va_list vargs;
859859
860860
if(JS_BUNDLED == builtin_get_js_delivery_mode()){
@@ -864,12 +864,12 @@
864864
builtin_fulfill_js_requests();
865865
}
866866
return;
867867
}
868868
va_start(vargs,zApi);
869
- for( zArg = zApi; zArg!=0; (zArg = va_arg (vargs, const char *))){
869
+ for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){
870870
if(0==builtin_emit_fossil_js_once(zArg)){
871871
fossil_fatal("Unknown fossil JS module: %s\n", zArg);
872872
}
873873
}
874874
va_end(vargs);
875875
}
876876
--- src/builtin.c
+++ src/builtin.c
@@ -843,18 +843,18 @@
843 ** builtin_request_js()) with any other app-/page-specific JS it may
844 ** need.
845 **
846 ** Example usage:
847 **
848 ** builtin_fossil_js_bundle_or("dom", "fetch", 0);
849 **
850 ** In bundled mode, that will (the first time it is called) emit all
851 ** builtin fossil JS APIs and "fulfill" the queue immediately. In
852 ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be
853 ** emitted the next time builtin_fulfill_js_requests() is called.
854 */
855 void builtin_fossil_js_bundle_or( const char * zApi, ... ) {
856 static int bundled = 0;
857 const char *zArg;
858 va_list vargs;
859
860 if(JS_BUNDLED == builtin_get_js_delivery_mode()){
@@ -864,12 +864,12 @@
864 builtin_fulfill_js_requests();
865 }
866 return;
867 }
868 va_start(vargs,zApi);
869 for( zArg = zApi; zArg!=0; (zArg = va_arg (vargs, const char *))){
870 if(0==builtin_emit_fossil_js_once(zArg)){
871 fossil_fatal("Unknown fossil JS module: %s\n", zArg);
872 }
873 }
874 va_end(vargs);
875 }
876
--- src/builtin.c
+++ src/builtin.c
@@ -843,18 +843,18 @@
843 ** builtin_request_js()) with any other app-/page-specific JS it may
844 ** need.
845 **
846 ** Example usage:
847 **
848 ** builtin_fossil_js_bundle_or("dom", "fetch", NULL);
849 **
850 ** In bundled mode, that will (the first time it is called) emit all
851 ** builtin fossil JS APIs and "fulfill" the queue immediately. In
852 ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be
853 ** emitted the next time builtin_fulfill_js_requests() is called.
854 */
855 NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) {
856 static int bundled = 0;
857 const char *zArg;
858 va_list vargs;
859
860 if(JS_BUNDLED == builtin_get_js_delivery_mode()){
@@ -864,12 +864,12 @@
864 builtin_fulfill_js_requests();
865 }
866 return;
867 }
868 va_start(vargs,zApi);
869 for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){
870 if(0==builtin_emit_fossil_js_once(zArg)){
871 fossil_fatal("Unknown fossil JS module: %s\n", zArg);
872 }
873 }
874 va_end(vargs);
875 }
876
+2 -2
--- src/checkout.c
+++ src/checkout.c
@@ -49,13 +49,13 @@
4949
void uncheckout(int vid){
5050
char *zPwd;
5151
if( vid<=0 ) return;
5252
sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
5353
file_dirname_sql_function, 0, 0);
54
- sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0,
54
+ sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0,
5555
file_delete_sql_function, 0, 0);
56
- sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0,
56
+ sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
5757
file_rmdir_sql_function, 0, 0);
5858
db_multi_exec(
5959
"CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
6060
filename_collation()
6161
);
6262
--- src/checkout.c
+++ src/checkout.c
@@ -49,13 +49,13 @@
49 void uncheckout(int vid){
50 char *zPwd;
51 if( vid<=0 ) return;
52 sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
53 file_dirname_sql_function, 0, 0);
54 sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0,
55 file_delete_sql_function, 0, 0);
56 sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0,
57 file_rmdir_sql_function, 0, 0);
58 db_multi_exec(
59 "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
60 filename_collation()
61 );
62
--- src/checkout.c
+++ src/checkout.c
@@ -49,13 +49,13 @@
49 void uncheckout(int vid){
50 char *zPwd;
51 if( vid<=0 ) return;
52 sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
53 file_dirname_sql_function, 0, 0);
54 sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0,
55 file_delete_sql_function, 0, 0);
56 sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
57 file_rmdir_sql_function, 0, 0);
58 db_multi_exec(
59 "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
60 filename_collation()
61 );
62
--- src/config.h
+++ src/config.h
@@ -245,14 +245,17 @@
245245
/*
246246
** A marker for functions that never return.
247247
*/
248248
#if defined(__GNUC__) || defined(__clang__)
249249
# define NORETURN __attribute__((__noreturn__))
250
+# define NULL_SENTINEL __attribute__((sentinel))
250251
#elif defined(_MSC_VER) && (_MSC_VER >= 1310)
251252
# define NORETURN __declspec(noreturn)
253
+# define NULL_SENTINEL
252254
#else
253255
# define NORETURN
256
+# define NULL_SENTINEL
254257
#endif
255258
256259
/*
257260
** Number of elements in an array
258261
*/
259262
--- src/config.h
+++ src/config.h
@@ -245,14 +245,17 @@
245 /*
246 ** A marker for functions that never return.
247 */
248 #if defined(__GNUC__) || defined(__clang__)
249 # define NORETURN __attribute__((__noreturn__))
 
250 #elif defined(_MSC_VER) && (_MSC_VER >= 1310)
251 # define NORETURN __declspec(noreturn)
 
252 #else
253 # define NORETURN
 
254 #endif
255
256 /*
257 ** Number of elements in an array
258 */
259
--- src/config.h
+++ src/config.h
@@ -245,14 +245,17 @@
245 /*
246 ** A marker for functions that never return.
247 */
248 #if defined(__GNUC__) || defined(__clang__)
249 # define NORETURN __attribute__((__noreturn__))
250 # define NULL_SENTINEL __attribute__((sentinel))
251 #elif defined(_MSC_VER) && (_MSC_VER >= 1310)
252 # define NORETURN __declspec(noreturn)
253 # define NULL_SENTINEL
254 #else
255 # define NORETURN
256 # define NULL_SENTINEL
257 #endif
258
259 /*
260 ** Number of elements in an array
261 */
262
+36 -8
--- src/db.c
+++ src/db.c
@@ -1617,10 +1617,15 @@
16171617
);
16181618
if( rc!=SQLITE_OK ){
16191619
db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
16201620
}
16211621
db_maybe_set_encryption_key(db, zDbName);
1622
+ sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
1623
+ sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc);
1624
+ sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc);
1625
+ sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc);
1626
+ sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc);
16221627
sqlite3_busy_timeout(db, 15000);
16231628
sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
16241629
sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
16251630
sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
16261631
sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
@@ -1633,11 +1638,10 @@
16331638
);
16341639
if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
16351640
db_add_aux_functions(db);
16361641
re_add_sql_func(db); /* The REGEXP operator */
16371642
foci_register(db); /* The "files_of_checkin" virtual table */
1638
- sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
16391643
sqlite3_set_authorizer(db, db_top_authorizer, db);
16401644
return db;
16411645
}
16421646
16431647
@@ -2776,11 +2780,14 @@
27762780
}
27772781
if( zArg[0]=='-' ) return 0;
27782782
if( m & SQLITE_TRACE_PROFILE ){
27792783
sqlite3_int64 nNano = *(sqlite3_int64*)pX;
27802784
double rMillisec = 0.000001 * nNano;
2781
- sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms */\n", rMillisec);
2785
+ int nRun = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_RUN, 0);
2786
+ int nVmStep = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_VM_STEP, 1);
2787
+ sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms, %r run, %d vm-steps */\n",
2788
+ rMillisec, nRun, nVmStep);
27822789
}else{
27832790
zEnd[0] = '\n';
27842791
zEnd[1] = 0;
27852792
}
27862793
zSql = sqlite3_expanded_sql(pStmt);
@@ -3089,16 +3096,30 @@
30893096
*/
30903097
char *db_get(const char *zName, const char *zDefault){
30913098
char *z = 0;
30923099
const Setting *pSetting = db_find_setting(zName, 0);
30933100
if( g.repositoryOpen ){
3094
- z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
3101
+ static Stmt q1;
3102
+ const char *zRes;
3103
+ db_static_prepare(&q1, "SELECT value FROM config WHERE name=$n");
3104
+ db_bind_text(&q1, "$n", zName);
3105
+ if( db_step(&q1)==SQLITE_ROW && (zRes = db_column_text(&q1,0))!=0 ){
3106
+ z = fossil_strdup(zRes);
3107
+ }
3108
+ db_reset(&q1);
30953109
}
30963110
if( z==0 && g.zConfigDbName ){
3111
+ static Stmt q2;
3112
+ const char *zRes;
30973113
db_swap_connections();
3098
- z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
3114
+ db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n");
30993115
db_swap_connections();
3116
+ db_bind_text(&q2, "$n", zName);
3117
+ if( db_step(&q2)==SQLITE_ROW && (zRes = db_column_text(&q2,0))!=0 ){
3118
+ z = fossil_strdup(zRes);
3119
+ }
3120
+ db_reset(&q2);
31003121
}
31013122
if( pSetting!=0 && pSetting->versionable ){
31023123
/* This is a versionable setting, try and get the info from a
31033124
** checked out file */
31043125
char * zZ = z;
@@ -3174,24 +3195,31 @@
31743195
}
31753196
int db_get_int(const char *zName, int dflt){
31763197
int v = dflt;
31773198
int rc;
31783199
if( g.repositoryOpen ){
3179
- Stmt q;
3180
- db_prepare(&q, "SELECT value FROM config WHERE name=%Q", zName);
3200
+ static Stmt q;
3201
+ db_static_prepare(&q, "SELECT value FROM config WHERE name=$n");
3202
+ db_bind_text(&q, "$n", zName);
31813203
rc = db_step(&q);
31823204
if( rc==SQLITE_ROW ){
31833205
v = db_column_int(&q, 0);
31843206
}
3185
- db_finalize(&q);
3207
+ db_reset(&q);
31863208
}else{
31873209
rc = SQLITE_DONE;
31883210
}
31893211
if( rc==SQLITE_DONE && g.zConfigDbName ){
3212
+ static Stmt q2;
31903213
db_swap_connections();
3191
- v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName);
3214
+ db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n");
31923215
db_swap_connections();
3216
+ db_bind_text(&q2, "$n", zName);
3217
+ if( db_step(&q2)==SQLITE_ROW ){
3218
+ v = db_column_int(&q2, 0);
3219
+ }
3220
+ db_reset(&q2);
31933221
}
31943222
return v;
31953223
}
31963224
void db_set_int(const char *zName, int value, int globalFlag){
31973225
db_assert_protection_off_or_not_sensitive(zName);
31983226
--- src/db.c
+++ src/db.c
@@ -1617,10 +1617,15 @@
1617 );
1618 if( rc!=SQLITE_OK ){
1619 db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
1620 }
1621 db_maybe_set_encryption_key(db, zDbName);
 
 
 
 
 
1622 sqlite3_busy_timeout(db, 15000);
1623 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
1624 sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
1625 sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
1626 sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
@@ -1633,11 +1638,10 @@
1633 );
1634 if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
1635 db_add_aux_functions(db);
1636 re_add_sql_func(db); /* The REGEXP operator */
1637 foci_register(db); /* The "files_of_checkin" virtual table */
1638 sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
1639 sqlite3_set_authorizer(db, db_top_authorizer, db);
1640 return db;
1641 }
1642
1643
@@ -2776,11 +2780,14 @@
2776 }
2777 if( zArg[0]=='-' ) return 0;
2778 if( m & SQLITE_TRACE_PROFILE ){
2779 sqlite3_int64 nNano = *(sqlite3_int64*)pX;
2780 double rMillisec = 0.000001 * nNano;
2781 sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms */\n", rMillisec);
 
 
 
2782 }else{
2783 zEnd[0] = '\n';
2784 zEnd[1] = 0;
2785 }
2786 zSql = sqlite3_expanded_sql(pStmt);
@@ -3089,16 +3096,30 @@
3089 */
3090 char *db_get(const char *zName, const char *zDefault){
3091 char *z = 0;
3092 const Setting *pSetting = db_find_setting(zName, 0);
3093 if( g.repositoryOpen ){
3094 z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
 
 
 
 
 
 
 
3095 }
3096 if( z==0 && g.zConfigDbName ){
 
 
3097 db_swap_connections();
3098 z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
3099 db_swap_connections();
 
 
 
 
 
3100 }
3101 if( pSetting!=0 && pSetting->versionable ){
3102 /* This is a versionable setting, try and get the info from a
3103 ** checked out file */
3104 char * zZ = z;
@@ -3174,24 +3195,31 @@
3174 }
3175 int db_get_int(const char *zName, int dflt){
3176 int v = dflt;
3177 int rc;
3178 if( g.repositoryOpen ){
3179 Stmt q;
3180 db_prepare(&q, "SELECT value FROM config WHERE name=%Q", zName);
 
3181 rc = db_step(&q);
3182 if( rc==SQLITE_ROW ){
3183 v = db_column_int(&q, 0);
3184 }
3185 db_finalize(&q);
3186 }else{
3187 rc = SQLITE_DONE;
3188 }
3189 if( rc==SQLITE_DONE && g.zConfigDbName ){
 
3190 db_swap_connections();
3191 v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName);
3192 db_swap_connections();
 
 
 
 
 
3193 }
3194 return v;
3195 }
3196 void db_set_int(const char *zName, int value, int globalFlag){
3197 db_assert_protection_off_or_not_sensitive(zName);
3198
--- src/db.c
+++ src/db.c
@@ -1617,10 +1617,15 @@
1617 );
1618 if( rc!=SQLITE_OK ){
1619 db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
1620 }
1621 db_maybe_set_encryption_key(db, zDbName);
1622 sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
1623 sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc);
1624 sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc);
1625 sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc);
1626 sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc);
1627 sqlite3_busy_timeout(db, 15000);
1628 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
1629 sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
1630 sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
1631 sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
@@ -1633,11 +1638,10 @@
1638 );
1639 if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
1640 db_add_aux_functions(db);
1641 re_add_sql_func(db); /* The REGEXP operator */
1642 foci_register(db); /* The "files_of_checkin" virtual table */
 
1643 sqlite3_set_authorizer(db, db_top_authorizer, db);
1644 return db;
1645 }
1646
1647
@@ -2776,11 +2780,14 @@
2780 }
2781 if( zArg[0]=='-' ) return 0;
2782 if( m & SQLITE_TRACE_PROFILE ){
2783 sqlite3_int64 nNano = *(sqlite3_int64*)pX;
2784 double rMillisec = 0.000001 * nNano;
2785 int nRun = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_RUN, 0);
2786 int nVmStep = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_VM_STEP, 1);
2787 sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms, %r run, %d vm-steps */\n",
2788 rMillisec, nRun, nVmStep);
2789 }else{
2790 zEnd[0] = '\n';
2791 zEnd[1] = 0;
2792 }
2793 zSql = sqlite3_expanded_sql(pStmt);
@@ -3089,16 +3096,30 @@
3096 */
3097 char *db_get(const char *zName, const char *zDefault){
3098 char *z = 0;
3099 const Setting *pSetting = db_find_setting(zName, 0);
3100 if( g.repositoryOpen ){
3101 static Stmt q1;
3102 const char *zRes;
3103 db_static_prepare(&q1, "SELECT value FROM config WHERE name=$n");
3104 db_bind_text(&q1, "$n", zName);
3105 if( db_step(&q1)==SQLITE_ROW && (zRes = db_column_text(&q1,0))!=0 ){
3106 z = fossil_strdup(zRes);
3107 }
3108 db_reset(&q1);
3109 }
3110 if( z==0 && g.zConfigDbName ){
3111 static Stmt q2;
3112 const char *zRes;
3113 db_swap_connections();
3114 db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n");
3115 db_swap_connections();
3116 db_bind_text(&q2, "$n", zName);
3117 if( db_step(&q2)==SQLITE_ROW && (zRes = db_column_text(&q2,0))!=0 ){
3118 z = fossil_strdup(zRes);
3119 }
3120 db_reset(&q2);
3121 }
3122 if( pSetting!=0 && pSetting->versionable ){
3123 /* This is a versionable setting, try and get the info from a
3124 ** checked out file */
3125 char * zZ = z;
@@ -3174,24 +3195,31 @@
3195 }
3196 int db_get_int(const char *zName, int dflt){
3197 int v = dflt;
3198 int rc;
3199 if( g.repositoryOpen ){
3200 static Stmt q;
3201 db_static_prepare(&q, "SELECT value FROM config WHERE name=$n");
3202 db_bind_text(&q, "$n", zName);
3203 rc = db_step(&q);
3204 if( rc==SQLITE_ROW ){
3205 v = db_column_int(&q, 0);
3206 }
3207 db_reset(&q);
3208 }else{
3209 rc = SQLITE_DONE;
3210 }
3211 if( rc==SQLITE_DONE && g.zConfigDbName ){
3212 static Stmt q2;
3213 db_swap_connections();
3214 db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n");
3215 db_swap_connections();
3216 db_bind_text(&q2, "$n", zName);
3217 if( db_step(&q2)==SQLITE_ROW ){
3218 v = db_column_int(&q2, 0);
3219 }
3220 db_reset(&q2);
3221 }
3222 return v;
3223 }
3224 void db_set_int(const char *zName, int value, int globalFlag){
3225 db_assert_protection_off_or_not_sensitive(zName);
3226
+11 -5
--- src/dispatch.c
+++ src/dispatch.c
@@ -1008,13 +1008,15 @@
10081008
** -w|--www List all web pages
10091009
**
10101010
** These options can be used when TOPIC is present:
10111011
**
10121012
** -h|--html Format output as HTML rather than plain text
1013
+** -c|--commands Restrict TOPIC search to commands
10131014
*/
10141015
void help_cmd(void){
10151016
int rc;
1017
+ int mask = CMDFLAG_ANY;
10161018
int isPage = 0;
10171019
const char *z;
10181020
const char *zCmdOrPage;
10191021
const CmdOrPage *pCmd = 0;
10201022
int useHtml = 0;
@@ -1056,32 +1058,36 @@
10561058
}
10571059
useHtml = find_option("html","h",0)!=0;
10581060
isPage = ('/' == *g.argv[2]) ? 1 : 0;
10591061
if(isPage){
10601062
zCmdOrPage = "page";
1063
+ }else if( find_option("commands","c",0)!=0 ){
1064
+ mask = CMDFLAG_COMMAND;
1065
+ zCmdOrPage = "command";
10611066
}else{
10621067
zCmdOrPage = "command or setting";
10631068
}
1064
- rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
1069
+ rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
10651070
if( rc ){
10661071
int i, n;
10671072
const char *az[5];
10681073
if( rc==1 ){
10691074
fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
10701075
}else{
10711076
fossil_print("ambiguous %s prefix: %s\n",
10721077
zCmdOrPage, g.argv[2]);
10731078
}
1074
- fossil_print("Did you mean one of:\n");
1079
+ fossil_print("Did you mean one of these TOPICs:\n");
10751080
n = dispatch_approx_match(g.argv[2], 5, az);
10761081
for(i=0; i<n; i++){
10771082
fossil_print(" * %s\n", az[i]);
10781083
}
10791084
fossil_print("Also consider using:\n");
1080
- fossil_print(" fossil help -a ;# show all commands\n");
1081
- fossil_print(" fossil help -w ;# show all web-pages\n");
1082
- fossil_print(" fossil help -s ;# show all settings\n");
1085
+ fossil_print(" fossil help TOPIC ;# show help on TOPIC\n");
1086
+ fossil_print(" fossil help -a ;# show all commands\n");
1087
+ fossil_print(" fossil help -w ;# show all web-pages\n");
1088
+ fossil_print(" fossil help -s ;# show all settings\n");
10831089
fossil_exit(1);
10841090
}
10851091
z = pCmd->zHelp;
10861092
if( z==0 ){
10871093
fossil_fatal("no help available for the %s %s",
10881094
--- src/dispatch.c
+++ src/dispatch.c
@@ -1008,13 +1008,15 @@
1008 ** -w|--www List all web pages
1009 **
1010 ** These options can be used when TOPIC is present:
1011 **
1012 ** -h|--html Format output as HTML rather than plain text
 
1013 */
1014 void help_cmd(void){
1015 int rc;
 
1016 int isPage = 0;
1017 const char *z;
1018 const char *zCmdOrPage;
1019 const CmdOrPage *pCmd = 0;
1020 int useHtml = 0;
@@ -1056,32 +1058,36 @@
1056 }
1057 useHtml = find_option("html","h",0)!=0;
1058 isPage = ('/' == *g.argv[2]) ? 1 : 0;
1059 if(isPage){
1060 zCmdOrPage = "page";
 
 
 
1061 }else{
1062 zCmdOrPage = "command or setting";
1063 }
1064 rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
1065 if( rc ){
1066 int i, n;
1067 const char *az[5];
1068 if( rc==1 ){
1069 fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
1070 }else{
1071 fossil_print("ambiguous %s prefix: %s\n",
1072 zCmdOrPage, g.argv[2]);
1073 }
1074 fossil_print("Did you mean one of:\n");
1075 n = dispatch_approx_match(g.argv[2], 5, az);
1076 for(i=0; i<n; i++){
1077 fossil_print(" * %s\n", az[i]);
1078 }
1079 fossil_print("Also consider using:\n");
1080 fossil_print(" fossil help -a ;# show all commands\n");
1081 fossil_print(" fossil help -w ;# show all web-pages\n");
1082 fossil_print(" fossil help -s ;# show all settings\n");
 
1083 fossil_exit(1);
1084 }
1085 z = pCmd->zHelp;
1086 if( z==0 ){
1087 fossil_fatal("no help available for the %s %s",
1088
--- src/dispatch.c
+++ src/dispatch.c
@@ -1008,13 +1008,15 @@
1008 ** -w|--www List all web pages
1009 **
1010 ** These options can be used when TOPIC is present:
1011 **
1012 ** -h|--html Format output as HTML rather than plain text
1013 ** -c|--commands Restrict TOPIC search to commands
1014 */
1015 void help_cmd(void){
1016 int rc;
1017 int mask = CMDFLAG_ANY;
1018 int isPage = 0;
1019 const char *z;
1020 const char *zCmdOrPage;
1021 const CmdOrPage *pCmd = 0;
1022 int useHtml = 0;
@@ -1056,32 +1058,36 @@
1058 }
1059 useHtml = find_option("html","h",0)!=0;
1060 isPage = ('/' == *g.argv[2]) ? 1 : 0;
1061 if(isPage){
1062 zCmdOrPage = "page";
1063 }else if( find_option("commands","c",0)!=0 ){
1064 mask = CMDFLAG_COMMAND;
1065 zCmdOrPage = "command";
1066 }else{
1067 zCmdOrPage = "command or setting";
1068 }
1069 rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
1070 if( rc ){
1071 int i, n;
1072 const char *az[5];
1073 if( rc==1 ){
1074 fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
1075 }else{
1076 fossil_print("ambiguous %s prefix: %s\n",
1077 zCmdOrPage, g.argv[2]);
1078 }
1079 fossil_print("Did you mean one of these TOPICs:\n");
1080 n = dispatch_approx_match(g.argv[2], 5, az);
1081 for(i=0; i<n; i++){
1082 fossil_print(" * %s\n", az[i]);
1083 }
1084 fossil_print("Also consider using:\n");
1085 fossil_print(" fossil help TOPIC ;# show help on TOPIC\n");
1086 fossil_print(" fossil help -a ;# show all commands\n");
1087 fossil_print(" fossil help -w ;# show all web-pages\n");
1088 fossil_print(" fossil help -s ;# show all settings\n");
1089 fossil_exit(1);
1090 }
1091 z = pCmd->zHelp;
1092 if( z==0 ){
1093 fossil_fatal("no help available for the %s %s",
1094
+1 -1
--- src/doc.c
+++ src/doc.c
@@ -408,11 +408,11 @@
408408
** no-ops.
409409
*/
410410
void document_emit_js(void){
411411
static int once = 0;
412412
if(0==once++){
413
- builtin_fossil_js_bundle_or("pikchr", 0);
413
+ builtin_fossil_js_bundle_or("pikchr", NULL);
414414
style_script_begin(__FILE__,__LINE__);
415415
CX("window.addEventListener('load', "
416416
"()=>window.fossil.pikchr.addSrcView(), "
417417
"false);\n");
418418
style_script_end();
419419
--- src/doc.c
+++ src/doc.c
@@ -408,11 +408,11 @@
408 ** no-ops.
409 */
410 void document_emit_js(void){
411 static int once = 0;
412 if(0==once++){
413 builtin_fossil_js_bundle_or("pikchr", 0);
414 style_script_begin(__FILE__,__LINE__);
415 CX("window.addEventListener('load', "
416 "()=>window.fossil.pikchr.addSrcView(), "
417 "false);\n");
418 style_script_end();
419
--- src/doc.c
+++ src/doc.c
@@ -408,11 +408,11 @@
408 ** no-ops.
409 */
410 void document_emit_js(void){
411 static int once = 0;
412 if(0==once++){
413 builtin_fossil_js_bundle_or("pikchr", NULL);
414 style_script_begin(__FILE__,__LINE__);
415 CX("window.addEventListener('load', "
416 "()=>window.fossil.pikchr.addSrcView(), "
417 "false);\n");
418 style_script_end();
419
+23 -17
--- src/encode.c
+++ src/encode.c
@@ -26,31 +26,38 @@
2626
** to a new string obtained from malloc().
2727
**
2828
** We also encode " as &quot; and ' as &#39; so they can appear as an argument
2929
** to markup.
3030
*/
31
-char *htmlize(const char *zIn, int n){
32
- int c;
31
+char *htmlize(const char *z, int n){
32
+ unsigned char c;
3333
int i = 0;
3434
int count = 0;
35
- char *zOut;
36
-
37
- if( n<0 ) n = strlen(zIn);
38
- while( i<n && (c = zIn[i])!=0 ){
39
- switch( c ){
40
- case '<': count += 4; break;
41
- case '>': count += 4; break;
42
- case '&': count += 5; break;
43
- case '"': count += 6; break;
44
- case '\'': count += 5; break;
45
- default: count++; break;
35
+ unsigned char *zOut;
36
+ const unsigned char *zIn = (const unsigned char*)z;
37
+
38
+ if( n<0 ) n = strlen(z);
39
+ while( i<n ){
40
+ switch( zIn[i] ){
41
+ case '<': count += 3; break;
42
+ case '>': count += 3; break;
43
+ case '&': count += 4; break;
44
+ case '"': count += 5; break;
45
+ case '\'': count += 4; break;
46
+ case 0: n = i; break;
4647
}
4748
i++;
4849
}
4950
i = 0;
50
- zOut = fossil_malloc( count+1 );
51
- while( n-->0 && (c = *zIn)!=0 ){
51
+ zOut = fossil_malloc( count+n+1 );
52
+ if( count==0 ){
53
+ memcpy(zOut, zIn, n);
54
+ zOut[n] = 0;
55
+ return (char*)zOut;
56
+ }
57
+ while( n-->0 ){
58
+ c = *(zIn++);
5259
switch( c ){
5360
case '<':
5461
zOut[i++] = '&';
5562
zOut[i++] = 'l';
5663
zOut[i++] = 't';
@@ -86,14 +93,13 @@
8693
break;
8794
default:
8895
zOut[i++] = c;
8996
break;
9097
}
91
- zIn++;
9298
}
9399
zOut[i] = 0;
94
- return zOut;
100
+ return (char*)zOut;
95101
}
96102
97103
/*
98104
** Append HTML-escaped text to a Blob.
99105
*/
100106
--- src/encode.c
+++ src/encode.c
@@ -26,31 +26,38 @@
26 ** to a new string obtained from malloc().
27 **
28 ** We also encode " as &quot; and ' as &#39; so they can appear as an argument
29 ** to markup.
30 */
31 char *htmlize(const char *zIn, int n){
32 int c;
33 int i = 0;
34 int count = 0;
35 char *zOut;
36
37 if( n<0 ) n = strlen(zIn);
38 while( i<n && (c = zIn[i])!=0 ){
39 switch( c ){
40 case '<': count += 4; break;
41 case '>': count += 4; break;
42 case '&': count += 5; break;
43 case '"': count += 6; break;
44 case '\'': count += 5; break;
45 default: count++; break;
 
46 }
47 i++;
48 }
49 i = 0;
50 zOut = fossil_malloc( count+1 );
51 while( n-->0 && (c = *zIn)!=0 ){
 
 
 
 
 
 
52 switch( c ){
53 case '<':
54 zOut[i++] = '&';
55 zOut[i++] = 'l';
56 zOut[i++] = 't';
@@ -86,14 +93,13 @@
86 break;
87 default:
88 zOut[i++] = c;
89 break;
90 }
91 zIn++;
92 }
93 zOut[i] = 0;
94 return zOut;
95 }
96
97 /*
98 ** Append HTML-escaped text to a Blob.
99 */
100
--- src/encode.c
+++ src/encode.c
@@ -26,31 +26,38 @@
26 ** to a new string obtained from malloc().
27 **
28 ** We also encode " as &quot; and ' as &#39; so they can appear as an argument
29 ** to markup.
30 */
31 char *htmlize(const char *z, int n){
32 unsigned char c;
33 int i = 0;
34 int count = 0;
35 unsigned char *zOut;
36 const unsigned char *zIn = (const unsigned char*)z;
37
38 if( n<0 ) n = strlen(z);
39 while( i<n ){
40 switch( zIn[i] ){
41 case '<': count += 3; break;
42 case '>': count += 3; break;
43 case '&': count += 4; break;
44 case '"': count += 5; break;
45 case '\'': count += 4; break;
46 case 0: n = i; break;
47 }
48 i++;
49 }
50 i = 0;
51 zOut = fossil_malloc( count+n+1 );
52 if( count==0 ){
53 memcpy(zOut, zIn, n);
54 zOut[n] = 0;
55 return (char*)zOut;
56 }
57 while( n-->0 ){
58 c = *(zIn++);
59 switch( c ){
60 case '<':
61 zOut[i++] = '&';
62 zOut[i++] = 'l';
63 zOut[i++] = 't';
@@ -86,14 +93,13 @@
93 break;
94 default:
95 zOut[i++] = c;
96 break;
97 }
 
98 }
99 zOut[i] = 0;
100 return (char*)zOut;
101 }
102
103 /*
104 ** Append HTML-escaped text to a Blob.
105 */
106
+10
--- src/export.c
+++ src/export.c
@@ -1170,10 +1170,11 @@
11701170
/*
11711171
** Check for 'fx_' table from previous Git import, otherwise take contact info
11721172
** from user table for <emailaddr> in committer field. If no emailaddr, check
11731173
** if username is in email form, otherwise use generic '[email protected]'.
11741174
*/
1175
+ char *zTmp;
11751176
if (db_table_exists("repository", "fx_git")) {
11761177
zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser);
11771178
} else {
11781179
zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser);
11791180
}
@@ -1183,10 +1184,15 @@
11831184
if (strchr(pMan->zUser, '@') == NULL) {
11841185
zEmail = mprintf("%[email protected]", pMan->zUser);
11851186
} else {
11861187
zEmail = fossil_strdup(pMan->zUser);
11871188
}
1189
+ } else if ((zTmp = strchr(zEmail, '<')) != NULL) {
1190
+ ++zTmp;
1191
+ char *zTmpEnd = strchr(zTmp, '>');
1192
+ *(zTmpEnd) = '\0';
1193
+ zEmail = fossil_strdup(zTmp);
11881194
}
11891195
fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf);
11901196
fossil_free(zEmail);
11911197
blob_init(&comment, pMan->zComment, -1);
11921198
if( blob_size(&comment)==0 ){
@@ -1294,10 +1300,11 @@
12941300
double rEnd; /* time of most recent export */
12951301
int rc; /* Result code */
12961302
int bForce; /* Do the export and sync even if no changes*/
12971303
int bNeedRepack = 0; /* True if we should run repack at the end */
12981304
int fManifest; /* Current "manifest" setting */
1305
+ int bIfExists; /* The --if-mirrored flag */
12991306
FILE *xCmd; /* Pipe to the "git fast-import" command */
13001307
FILE *pMarks; /* Git mark files */
13011308
Stmt q; /* Queries */
13021309
char zLine[200]; /* One line of a mark file */
13031310
@@ -1308,10 +1315,11 @@
13081315
nLimit = (unsigned int)atoi(zLimit);
13091316
if( nLimit<=0 ) fossil_fatal("--limit must be positive");
13101317
}
13111318
zAutoPush = find_option("autopush",0,1);
13121319
bForce = find_option("force","f",0)!=0;
1320
+ bIfExists = find_option("if-mirrored",0,0)!=0;
13131321
gitmirror_verbosity = VERB_NORMAL;
13141322
while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; }
13151323
while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; }
13161324
verify_all_options();
13171325
if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); }
@@ -1321,10 +1329,11 @@
13211329
db_set("last-git-export-repo", blob_str(&mirror), 0);
13221330
blob_reset(&mirror);
13231331
}
13241332
zMirror = db_get("last-git-export-repo", 0);
13251333
if( zMirror==0 ){
1334
+ if( bIfExists ) return;
13261335
fossil_fatal("no Git repository specified");
13271336
}
13281337
13291338
/* Make sure the GIT repository directory exists */
13301339
rc = file_mkdir(zMirror, ExtFILE, 0);
@@ -1694,10 +1703,11 @@
16941703
** to the same repository. Or if URL is "off" the
16951704
** auto-push mechanism is disabled
16961705
** --debug FILE Write fast-export text to FILE rather than
16971706
** piping it into "git fast-import".
16981707
** --force|-f Do the export even if nothing has changed
1708
+** --if-mirrored No-op if the mirror does not already exist.
16991709
** --limit N Add no more than N new check-ins to MIRROR.
17001710
** Useful for debugging
17011711
** --quiet|-q Reduce output. Repeat for even less output.
17021712
** --verbose|-v More output.
17031713
**
17041714
--- src/export.c
+++ src/export.c
@@ -1170,10 +1170,11 @@
1170 /*
1171 ** Check for 'fx_' table from previous Git import, otherwise take contact info
1172 ** from user table for <emailaddr> in committer field. If no emailaddr, check
1173 ** if username is in email form, otherwise use generic '[email protected]'.
1174 */
 
1175 if (db_table_exists("repository", "fx_git")) {
1176 zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser);
1177 } else {
1178 zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser);
1179 }
@@ -1183,10 +1184,15 @@
1183 if (strchr(pMan->zUser, '@') == NULL) {
1184 zEmail = mprintf("%[email protected]", pMan->zUser);
1185 } else {
1186 zEmail = fossil_strdup(pMan->zUser);
1187 }
 
 
 
 
 
1188 }
1189 fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf);
1190 fossil_free(zEmail);
1191 blob_init(&comment, pMan->zComment, -1);
1192 if( blob_size(&comment)==0 ){
@@ -1294,10 +1300,11 @@
1294 double rEnd; /* time of most recent export */
1295 int rc; /* Result code */
1296 int bForce; /* Do the export and sync even if no changes*/
1297 int bNeedRepack = 0; /* True if we should run repack at the end */
1298 int fManifest; /* Current "manifest" setting */
 
1299 FILE *xCmd; /* Pipe to the "git fast-import" command */
1300 FILE *pMarks; /* Git mark files */
1301 Stmt q; /* Queries */
1302 char zLine[200]; /* One line of a mark file */
1303
@@ -1308,10 +1315,11 @@
1308 nLimit = (unsigned int)atoi(zLimit);
1309 if( nLimit<=0 ) fossil_fatal("--limit must be positive");
1310 }
1311 zAutoPush = find_option("autopush",0,1);
1312 bForce = find_option("force","f",0)!=0;
 
1313 gitmirror_verbosity = VERB_NORMAL;
1314 while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; }
1315 while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; }
1316 verify_all_options();
1317 if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); }
@@ -1321,10 +1329,11 @@
1321 db_set("last-git-export-repo", blob_str(&mirror), 0);
1322 blob_reset(&mirror);
1323 }
1324 zMirror = db_get("last-git-export-repo", 0);
1325 if( zMirror==0 ){
 
1326 fossil_fatal("no Git repository specified");
1327 }
1328
1329 /* Make sure the GIT repository directory exists */
1330 rc = file_mkdir(zMirror, ExtFILE, 0);
@@ -1694,10 +1703,11 @@
1694 ** to the same repository. Or if URL is "off" the
1695 ** auto-push mechanism is disabled
1696 ** --debug FILE Write fast-export text to FILE rather than
1697 ** piping it into "git fast-import".
1698 ** --force|-f Do the export even if nothing has changed
 
1699 ** --limit N Add no more than N new check-ins to MIRROR.
1700 ** Useful for debugging
1701 ** --quiet|-q Reduce output. Repeat for even less output.
1702 ** --verbose|-v More output.
1703 **
1704
--- src/export.c
+++ src/export.c
@@ -1170,10 +1170,11 @@
1170 /*
1171 ** Check for 'fx_' table from previous Git import, otherwise take contact info
1172 ** from user table for <emailaddr> in committer field. If no emailaddr, check
1173 ** if username is in email form, otherwise use generic '[email protected]'.
1174 */
1175 char *zTmp;
1176 if (db_table_exists("repository", "fx_git")) {
1177 zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser);
1178 } else {
1179 zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser);
1180 }
@@ -1183,10 +1184,15 @@
1184 if (strchr(pMan->zUser, '@') == NULL) {
1185 zEmail = mprintf("%[email protected]", pMan->zUser);
1186 } else {
1187 zEmail = fossil_strdup(pMan->zUser);
1188 }
1189 } else if ((zTmp = strchr(zEmail, '<')) != NULL) {
1190 ++zTmp;
1191 char *zTmpEnd = strchr(zTmp, '>');
1192 *(zTmpEnd) = '\0';
1193 zEmail = fossil_strdup(zTmp);
1194 }
1195 fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf);
1196 fossil_free(zEmail);
1197 blob_init(&comment, pMan->zComment, -1);
1198 if( blob_size(&comment)==0 ){
@@ -1294,10 +1300,11 @@
1300 double rEnd; /* time of most recent export */
1301 int rc; /* Result code */
1302 int bForce; /* Do the export and sync even if no changes*/
1303 int bNeedRepack = 0; /* True if we should run repack at the end */
1304 int fManifest; /* Current "manifest" setting */
1305 int bIfExists; /* The --if-mirrored flag */
1306 FILE *xCmd; /* Pipe to the "git fast-import" command */
1307 FILE *pMarks; /* Git mark files */
1308 Stmt q; /* Queries */
1309 char zLine[200]; /* One line of a mark file */
1310
@@ -1308,10 +1315,11 @@
1315 nLimit = (unsigned int)atoi(zLimit);
1316 if( nLimit<=0 ) fossil_fatal("--limit must be positive");
1317 }
1318 zAutoPush = find_option("autopush",0,1);
1319 bForce = find_option("force","f",0)!=0;
1320 bIfExists = find_option("if-mirrored",0,0)!=0;
1321 gitmirror_verbosity = VERB_NORMAL;
1322 while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; }
1323 while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; }
1324 verify_all_options();
1325 if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); }
@@ -1321,10 +1329,11 @@
1329 db_set("last-git-export-repo", blob_str(&mirror), 0);
1330 blob_reset(&mirror);
1331 }
1332 zMirror = db_get("last-git-export-repo", 0);
1333 if( zMirror==0 ){
1334 if( bIfExists ) return;
1335 fossil_fatal("no Git repository specified");
1336 }
1337
1338 /* Make sure the GIT repository directory exists */
1339 rc = file_mkdir(zMirror, ExtFILE, 0);
@@ -1694,10 +1703,11 @@
1703 ** to the same repository. Or if URL is "off" the
1704 ** auto-push mechanism is disabled
1705 ** --debug FILE Write fast-export text to FILE rather than
1706 ** piping it into "git fast-import".
1707 ** --force|-f Do the export even if nothing has changed
1708 ** --if-mirrored No-op if the mirror does not already exist.
1709 ** --limit N Add no more than N new check-ins to MIRROR.
1710 ** Useful for debugging
1711 ** --quiet|-q Reduce output. Repeat for even less output.
1712 ** --verbose|-v More output.
1713 **
1714
+1 -1
--- src/fileedit.c
+++ src/fileedit.c
@@ -1989,11 +1989,11 @@
19891989
}
19901990
CX("</div>"/*#fileedit-tab-help*/);
19911991
19921992
builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
19931993
"storage", "popupwidget", "copybutton",
1994
- "pikchr", 0);
1994
+ "pikchr", NULL);
19951995
/*
19961996
** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
19971997
** used for dynamically toggling certain UI components on and off.
19981998
** Must come after window.fossil has been intialized and before
19991999
** fossil.page.fileedit.js. Potential TODO: move this into the
20002000
--- src/fileedit.c
+++ src/fileedit.c
@@ -1989,11 +1989,11 @@
1989 }
1990 CX("</div>"/*#fileedit-tab-help*/);
1991
1992 builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
1993 "storage", "popupwidget", "copybutton",
1994 "pikchr", 0);
1995 /*
1996 ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
1997 ** used for dynamically toggling certain UI components on and off.
1998 ** Must come after window.fossil has been intialized and before
1999 ** fossil.page.fileedit.js. Potential TODO: move this into the
2000
--- src/fileedit.c
+++ src/fileedit.c
@@ -1989,11 +1989,11 @@
1989 }
1990 CX("</div>"/*#fileedit-tab-help*/);
1991
1992 builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
1993 "storage", "popupwidget", "copybutton",
1994 "pikchr", NULL);
1995 /*
1996 ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
1997 ** used for dynamically toggling certain UI components on and off.
1998 ** Must come after window.fossil has been intialized and before
1999 ** fossil.page.fileedit.js. Potential TODO: move this into the
2000
+9 -4
--- src/forum.c
+++ src/forum.c
@@ -287,11 +287,11 @@
287287
fpid = symbolic_name_to_rid(zName, "f");
288288
if( fpid<=0 ){
289289
fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName));
290290
}
291291
if( fpid<=0 ){
292
- fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName);
292
+ fossil_fatal("unknown or ambiguous forum id: \"%s\"", zName);
293293
}
294294
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
295295
if( froot==0 ){
296296
fossil_fatal("Not a forum post: \"%s\"", zName);
297297
}
@@ -743,11 +743,11 @@
743743
** Emit Forum Javascript which applies (or optionally can apply)
744744
** to all forum-related pages. It does not include page-specific
745745
** code (e.g. "forum.js").
746746
*/
747747
static void forum_emit_js(void){
748
- builtin_fossil_js_bundle_or("copybutton", "pikchr", 0);
748
+ builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL);
749749
builtin_request_js("fossil.page.forumpost.js");
750750
}
751751
752752
/*
753753
** WEBPAGE: forumpost
@@ -813,15 +813,20 @@
813813
if( zName==0 ){
814814
webpage_error("Missing \"name=\" query parameter");
815815
}
816816
fpid = symbolic_name_to_rid(zName, "f");
817817
if( fpid<=0 ){
818
- webpage_error("Unknown or ambiguous forum id: \"%s\"", zName);
818
+ if( fpid==0 ){
819
+ webpage_notfound_error("Unknown forum id: \"%s\"", zName);
820
+ }else{
821
+ ambiguous_page();
822
+ }
823
+ return;
819824
}
820825
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
821826
if( froot==0 ){
822
- webpage_error("Not a forum post: \"%s\"", zName);
827
+ webpage_notfound_error("Not a forum post: \"%s\"", zName);
823828
}
824829
if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
825830
826831
/* Decode the mode parameters. */
827832
if( bRaw ){
828833
--- src/forum.c
+++ src/forum.c
@@ -287,11 +287,11 @@
287 fpid = symbolic_name_to_rid(zName, "f");
288 if( fpid<=0 ){
289 fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName));
290 }
291 if( fpid<=0 ){
292 fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName);
293 }
294 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
295 if( froot==0 ){
296 fossil_fatal("Not a forum post: \"%s\"", zName);
297 }
@@ -743,11 +743,11 @@
743 ** Emit Forum Javascript which applies (or optionally can apply)
744 ** to all forum-related pages. It does not include page-specific
745 ** code (e.g. "forum.js").
746 */
747 static void forum_emit_js(void){
748 builtin_fossil_js_bundle_or("copybutton", "pikchr", 0);
749 builtin_request_js("fossil.page.forumpost.js");
750 }
751
752 /*
753 ** WEBPAGE: forumpost
@@ -813,15 +813,20 @@
813 if( zName==0 ){
814 webpage_error("Missing \"name=\" query parameter");
815 }
816 fpid = symbolic_name_to_rid(zName, "f");
817 if( fpid<=0 ){
818 webpage_error("Unknown or ambiguous forum id: \"%s\"", zName);
 
 
 
 
 
819 }
820 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
821 if( froot==0 ){
822 webpage_error("Not a forum post: \"%s\"", zName);
823 }
824 if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
825
826 /* Decode the mode parameters. */
827 if( bRaw ){
828
--- src/forum.c
+++ src/forum.c
@@ -287,11 +287,11 @@
287 fpid = symbolic_name_to_rid(zName, "f");
288 if( fpid<=0 ){
289 fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName));
290 }
291 if( fpid<=0 ){
292 fossil_fatal("unknown or ambiguous forum id: \"%s\"", zName);
293 }
294 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
295 if( froot==0 ){
296 fossil_fatal("Not a forum post: \"%s\"", zName);
297 }
@@ -743,11 +743,11 @@
743 ** Emit Forum Javascript which applies (or optionally can apply)
744 ** to all forum-related pages. It does not include page-specific
745 ** code (e.g. "forum.js").
746 */
747 static void forum_emit_js(void){
748 builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL);
749 builtin_request_js("fossil.page.forumpost.js");
750 }
751
752 /*
753 ** WEBPAGE: forumpost
@@ -813,15 +813,20 @@
813 if( zName==0 ){
814 webpage_error("Missing \"name=\" query parameter");
815 }
816 fpid = symbolic_name_to_rid(zName, "f");
817 if( fpid<=0 ){
818 if( fpid==0 ){
819 webpage_notfound_error("Unknown forum id: \"%s\"", zName);
820 }else{
821 ambiguous_page();
822 }
823 return;
824 }
825 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
826 if( froot==0 ){
827 webpage_notfound_error("Not a forum post: \"%s\"", zName);
828 }
829 if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
830
831 /* Decode the mode parameters. */
832 if( bRaw ){
833
+2 -1
--- src/import.c
+++ src/import.c
@@ -1947,15 +1947,16 @@
19471947
** The following 'fx_' table is used to hold information needed for
19481948
** importing and exporting to attribute Fossil check-ins or Git commits
19491949
** to either a desired username or full contact information string.
19501950
*/
19511951
if(ggit.nGitAttr > 0) {
1952
+ int idx;
19521953
db_unprotect(PROTECT_ALL);
19531954
db_multi_exec(
19541955
"CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);"
19551956
);
1956
- for( int idx = 0; idx < ggit.nGitAttr; ++idx ){
1957
+ for(idx = 0; idx < ggit.nGitAttr; ++idx ){
19571958
db_multi_exec(
19581959
"INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)",
19591960
ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail
19601961
);
19611962
}
19621963
--- src/import.c
+++ src/import.c
@@ -1947,15 +1947,16 @@
1947 ** The following 'fx_' table is used to hold information needed for
1948 ** importing and exporting to attribute Fossil check-ins or Git commits
1949 ** to either a desired username or full contact information string.
1950 */
1951 if(ggit.nGitAttr > 0) {
 
1952 db_unprotect(PROTECT_ALL);
1953 db_multi_exec(
1954 "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);"
1955 );
1956 for( int idx = 0; idx < ggit.nGitAttr; ++idx ){
1957 db_multi_exec(
1958 "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)",
1959 ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail
1960 );
1961 }
1962
--- src/import.c
+++ src/import.c
@@ -1947,15 +1947,16 @@
1947 ** The following 'fx_' table is used to hold information needed for
1948 ** importing and exporting to attribute Fossil check-ins or Git commits
1949 ** to either a desired username or full contact information string.
1950 */
1951 if(ggit.nGitAttr > 0) {
1952 int idx;
1953 db_unprotect(PROTECT_ALL);
1954 db_multi_exec(
1955 "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);"
1956 );
1957 for(idx = 0; idx < ggit.nGitAttr; ++idx ){
1958 db_multi_exec(
1959 "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)",
1960 ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail
1961 );
1962 }
1963
+4 -1
--- src/info.c
+++ src/info.c
@@ -2136,11 +2136,11 @@
21362136
if(includeJS && !emittedJS){
21372137
emittedJS = 1;
21382138
if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
21392139
builtin_request_js("scroll.js");
21402140
}
2141
- builtin_fossil_js_bundle_or("numbered-lines", 0);
2141
+ builtin_fossil_js_bundle_or("numbered-lines", NULL);
21422142
}
21432143
}
21442144
21452145
/*
21462146
** COMMAND: test-line-numbers
@@ -2398,14 +2398,17 @@
23982398
}
23992399
24002400
if( isFile ){
24012401
if( isSymbolicCI ){
24022402
zHeader = mprintf("%s at %s", file_tail(zName), zCI);
2403
+ style_set_current_page("doc/%t/%T", zCI, zName);
24032404
}else if( zCIUuid && zCIUuid[0] ){
24042405
zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2406
+ style_set_current_page("doc/%S/%T", zCIUuid, zName);
24052407
}else{
24062408
zHeader = mprintf("%s", file_tail(zName));
2409
+ style_set_current_page("doc/tip/%T", zName);
24072410
}
24082411
}else if( descOnly ){
24092412
zHeader = mprintf("Artifact Description [%S]", zUuid);
24102413
}else{
24112414
zHeader = mprintf("Artifact [%S]", zUuid);
24122415
--- src/info.c
+++ src/info.c
@@ -2136,11 +2136,11 @@
2136 if(includeJS && !emittedJS){
2137 emittedJS = 1;
2138 if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
2139 builtin_request_js("scroll.js");
2140 }
2141 builtin_fossil_js_bundle_or("numbered-lines", 0);
2142 }
2143 }
2144
2145 /*
2146 ** COMMAND: test-line-numbers
@@ -2398,14 +2398,17 @@
2398 }
2399
2400 if( isFile ){
2401 if( isSymbolicCI ){
2402 zHeader = mprintf("%s at %s", file_tail(zName), zCI);
 
2403 }else if( zCIUuid && zCIUuid[0] ){
2404 zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
 
2405 }else{
2406 zHeader = mprintf("%s", file_tail(zName));
 
2407 }
2408 }else if( descOnly ){
2409 zHeader = mprintf("Artifact Description [%S]", zUuid);
2410 }else{
2411 zHeader = mprintf("Artifact [%S]", zUuid);
2412
--- src/info.c
+++ src/info.c
@@ -2136,11 +2136,11 @@
2136 if(includeJS && !emittedJS){
2137 emittedJS = 1;
2138 if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
2139 builtin_request_js("scroll.js");
2140 }
2141 builtin_fossil_js_bundle_or("numbered-lines", NULL);
2142 }
2143 }
2144
2145 /*
2146 ** COMMAND: test-line-numbers
@@ -2398,14 +2398,17 @@
2398 }
2399
2400 if( isFile ){
2401 if( isSymbolicCI ){
2402 zHeader = mprintf("%s at %s", file_tail(zName), zCI);
2403 style_set_current_page("doc/%t/%T", zCI, zName);
2404 }else if( zCIUuid && zCIUuid[0] ){
2405 zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2406 style_set_current_page("doc/%S/%T", zCIUuid, zName);
2407 }else{
2408 zHeader = mprintf("%s", file_tail(zName));
2409 style_set_current_page("doc/tip/%T", zName);
2410 }
2411 }else if( descOnly ){
2412 zHeader = mprintf("Artifact Description [%S]", zUuid);
2413 }else{
2414 zHeader = mprintf("Artifact [%S]", zUuid);
2415
+9 -5
--- src/interwiki.c
+++ src/interwiki.c
@@ -74,23 +74,26 @@
7474
int nCode;
7575
int i;
7676
const char *zPage;
7777
int nPage;
7878
char *zUrl = 0;
79
- Stmt q;
79
+ char *zName;
80
+ static Stmt q;
8081
for(i=0; fossil_isalnum(zTarget[i]); i++){}
8182
if( zTarget[i]!=':' ) return 0;
8283
nCode = i;
8384
if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
8485
zPage = zTarget + nCode + 1;
8586
nPage = (int)strlen(zPage);
86
- db_prepare(&q,
87
+ db_static_prepare(&q,
8788
"SELECT json_extract(value,'$.base'),"
8889
" json_extract(value,'$.hash'),"
8990
" json_extract(value,'$.wiki')"
90
- " FROM config WHERE name=lower('interwiki:%.*q')",
91
- nCode, zTarget);
91
+ " FROM config WHERE name=lower($name)"
92
+ );
93
+ zName = mprintf("interwiki:%.*s", nCode, zTarget);
94
+ db_bind_text(&q, "$name", zName);
9295
while( db_step(&q)==SQLITE_ROW ){
9396
const char *zBase = db_column_text(&q,0);
9497
if( zBase==0 || zBase[0]==0 ) break;
9598
if( nPage==0 || zPage[0]=='/' ){
9699
/* Path */
@@ -108,11 +111,12 @@
108111
zUrl = mprintf("%s%s%s", zBase, zWiki, zPage);
109112
}
110113
}
111114
break;
112115
}
113
- db_finalize(&q);
116
+ db_reset(&q);
117
+ free(zName);
114118
return zUrl;
115119
}
116120
117121
/*
118122
** If hyperlink target zTarget begins with an interwiki tag that ought
119123
--- src/interwiki.c
+++ src/interwiki.c
@@ -74,23 +74,26 @@
74 int nCode;
75 int i;
76 const char *zPage;
77 int nPage;
78 char *zUrl = 0;
79 Stmt q;
 
80 for(i=0; fossil_isalnum(zTarget[i]); i++){}
81 if( zTarget[i]!=':' ) return 0;
82 nCode = i;
83 if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
84 zPage = zTarget + nCode + 1;
85 nPage = (int)strlen(zPage);
86 db_prepare(&q,
87 "SELECT json_extract(value,'$.base'),"
88 " json_extract(value,'$.hash'),"
89 " json_extract(value,'$.wiki')"
90 " FROM config WHERE name=lower('interwiki:%.*q')",
91 nCode, zTarget);
 
 
92 while( db_step(&q)==SQLITE_ROW ){
93 const char *zBase = db_column_text(&q,0);
94 if( zBase==0 || zBase[0]==0 ) break;
95 if( nPage==0 || zPage[0]=='/' ){
96 /* Path */
@@ -108,11 +111,12 @@
108 zUrl = mprintf("%s%s%s", zBase, zWiki, zPage);
109 }
110 }
111 break;
112 }
113 db_finalize(&q);
 
114 return zUrl;
115 }
116
117 /*
118 ** If hyperlink target zTarget begins with an interwiki tag that ought
119
--- src/interwiki.c
+++ src/interwiki.c
@@ -74,23 +74,26 @@
74 int nCode;
75 int i;
76 const char *zPage;
77 int nPage;
78 char *zUrl = 0;
79 char *zName;
80 static Stmt q;
81 for(i=0; fossil_isalnum(zTarget[i]); i++){}
82 if( zTarget[i]!=':' ) return 0;
83 nCode = i;
84 if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
85 zPage = zTarget + nCode + 1;
86 nPage = (int)strlen(zPage);
87 db_static_prepare(&q,
88 "SELECT json_extract(value,'$.base'),"
89 " json_extract(value,'$.hash'),"
90 " json_extract(value,'$.wiki')"
91 " FROM config WHERE name=lower($name)"
92 );
93 zName = mprintf("interwiki:%.*s", nCode, zTarget);
94 db_bind_text(&q, "$name", zName);
95 while( db_step(&q)==SQLITE_ROW ){
96 const char *zBase = db_column_text(&q,0);
97 if( zBase==0 || zBase[0]==0 ) break;
98 if( nPage==0 || zPage[0]=='/' ){
99 /* Path */
@@ -108,11 +111,12 @@
111 zUrl = mprintf("%s%s%s", zBase, zWiki, zPage);
112 }
113 }
114 break;
115 }
116 db_reset(&q);
117 free(zName);
118 return zUrl;
119 }
120
121 /*
122 ** If hyperlink target zTarget begins with an interwiki tag that ought
123
+6 -4
--- src/main.c
+++ src/main.c
@@ -673,10 +673,11 @@
673673
#endif
674674
}
675675
}
676676
#endif
677677
678
+ fossil_printf_selfcheck();
678679
fossil_limit_memory(1);
679680
if( sqlite3_libversion_number()<3034000 ){
680681
fossil_panic("Unsuitable SQLite version %s, must be at least 3.34.0",
681682
sqlite3_libversion());
682683
}
@@ -795,18 +796,19 @@
795796
/* If --help is found anywhere on the command line, translate the command
796797
* to "fossil help cmdname" where "cmdname" is the first argument that
797798
* does not begin with a "-" character. If all arguments start with "-",
798799
* translate to "fossil help argv[1] argv[2]...". */
799800
int i, nNewArgc;
800
- char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) );
801
+ char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) );
801802
zNewArgv[0] = g.argv[0];
802803
zNewArgv[1] = "help";
804
+ zNewArgv[2] = "-c";
803805
for(i=1; i<g.argc; i++){
804806
if( g.argv[i][0]!='-' ){
805
- nNewArgc = 3;
806
- zNewArgv[2] = g.argv[i];
807
- zNewArgv[3] = 0;
807
+ nNewArgc = 4;
808
+ zNewArgv[3] = g.argv[i];
809
+ zNewArgv[4] = 0;
808810
break;
809811
}
810812
}
811813
if( i==g.argc ){
812814
for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i];
813815
--- src/main.c
+++ src/main.c
@@ -673,10 +673,11 @@
673 #endif
674 }
675 }
676 #endif
677
 
678 fossil_limit_memory(1);
679 if( sqlite3_libversion_number()<3034000 ){
680 fossil_panic("Unsuitable SQLite version %s, must be at least 3.34.0",
681 sqlite3_libversion());
682 }
@@ -795,18 +796,19 @@
795 /* If --help is found anywhere on the command line, translate the command
796 * to "fossil help cmdname" where "cmdname" is the first argument that
797 * does not begin with a "-" character. If all arguments start with "-",
798 * translate to "fossil help argv[1] argv[2]...". */
799 int i, nNewArgc;
800 char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) );
801 zNewArgv[0] = g.argv[0];
802 zNewArgv[1] = "help";
 
803 for(i=1; i<g.argc; i++){
804 if( g.argv[i][0]!='-' ){
805 nNewArgc = 3;
806 zNewArgv[2] = g.argv[i];
807 zNewArgv[3] = 0;
808 break;
809 }
810 }
811 if( i==g.argc ){
812 for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i];
813
--- src/main.c
+++ src/main.c
@@ -673,10 +673,11 @@
673 #endif
674 }
675 }
676 #endif
677
678 fossil_printf_selfcheck();
679 fossil_limit_memory(1);
680 if( sqlite3_libversion_number()<3034000 ){
681 fossil_panic("Unsuitable SQLite version %s, must be at least 3.34.0",
682 sqlite3_libversion());
683 }
@@ -795,18 +796,19 @@
796 /* If --help is found anywhere on the command line, translate the command
797 * to "fossil help cmdname" where "cmdname" is the first argument that
798 * does not begin with a "-" character. If all arguments start with "-",
799 * translate to "fossil help argv[1] argv[2]...". */
800 int i, nNewArgc;
801 char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) );
802 zNewArgv[0] = g.argv[0];
803 zNewArgv[1] = "help";
804 zNewArgv[2] = "-c";
805 for(i=1; i<g.argc; i++){
806 if( g.argv[i][0]!='-' ){
807 nNewArgc = 4;
808 zNewArgv[3] = g.argv[i];
809 zNewArgv[4] = 0;
810 break;
811 }
812 }
813 if( i==g.argc ){
814 for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i];
815
+8 -37
--- src/manifest.c
+++ src/manifest.c
@@ -2358,14 +2358,14 @@
23582358
}
23592359
if( p->type==CFTYPE_WIKI ){
23602360
char *zTag = mprintf("wiki-%s", p->zWikiTitle);
23612361
int tagid = tag_findid(zTag, 1);
23622362
int prior;
2363
- char *zComment;
2364
- const char *zPrefix;
2363
+ char cPrefix;
23652364
int nWiki;
23662365
char zLength[40];
2366
+
23672367
while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
23682368
nWiki = strlen(p->zWiki);
23692369
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
23702370
tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
23712371
fossil_free(zTag);
@@ -2377,56 +2377,27 @@
23772377
);
23782378
if( prior ){
23792379
content_deltify(prior, &rid, 1, 0);
23802380
}
23812381
if( nWiki<=0 ){
2382
- zPrefix = "Deleted";
2382
+ cPrefix = '-';
23832383
}else if( !prior ){
2384
- zPrefix = "Added";
2384
+ cPrefix = '+';
23852385
}else{
2386
- zPrefix = "Changes to";
2387
- }
2388
- switch( wiki_page_type(p->zWikiTitle) ){
2389
- case WIKITYPE_CHECKIN: {
2390
- zComment = mprintf("%s wiki for check-in [%S]", zPrefix,
2391
- p->zWikiTitle+8);
2392
- break;
2393
- }
2394
- case WIKITYPE_BRANCH: {
2395
- zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]",
2396
- zPrefix, p->zWikiTitle+7, p->zWikiTitle+7);
2397
- break;
2398
- }
2399
- case WIKITYPE_TAG: {
2400
- zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]",
2401
- zPrefix, p->zWikiTitle+4, p->zWikiTitle+4);
2402
- break;
2403
- }
2404
- default: {
2405
- zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle);
2406
- break;
2407
- }
2386
+ cPrefix = ':';
24082387
}
24092388
search_doc_touch('w',rid,p->zWikiTitle);
24102389
if( manifest_crosslink_busy ){
24112390
add_pending_crosslink('w',p->zWikiTitle);
24122391
}else{
24132392
backlink_wiki_refresh(p->zWikiTitle);
24142393
}
24152394
db_multi_exec(
2416
- "REPLACE INTO event(type,mtime,objid,user,comment,"
2417
- " bgcolor,euser,ecomment)"
2418
- "VALUES('w',%.17g,%d,%Q,%Q,"
2419
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
2420
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
2421
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
2422
- p->rDate, rid, p->zUser, zComment,
2423
- TAG_BGCOLOR, rid,
2424
- TAG_USER, rid,
2425
- TAG_COMMENT, rid
2395
+ "REPLACE INTO event(type,mtime,objid,user,comment)"
2396
+ "VALUES('w',%.17g,%d,%Q,'%c%q');",
2397
+ p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle
24262398
);
2427
- fossil_free(zComment);
24282399
}
24292400
if( p->type==CFTYPE_EVENT ){
24302401
char *zTag = mprintf("event-%s", p->zEventId);
24312402
int tagid = tag_findid(zTag, 1);
24322403
int prior, subsequent;
24332404
--- src/manifest.c
+++ src/manifest.c
@@ -2358,14 +2358,14 @@
2358 }
2359 if( p->type==CFTYPE_WIKI ){
2360 char *zTag = mprintf("wiki-%s", p->zWikiTitle);
2361 int tagid = tag_findid(zTag, 1);
2362 int prior;
2363 char *zComment;
2364 const char *zPrefix;
2365 int nWiki;
2366 char zLength[40];
 
2367 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2368 nWiki = strlen(p->zWiki);
2369 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
2370 tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
2371 fossil_free(zTag);
@@ -2377,56 +2377,27 @@
2377 );
2378 if( prior ){
2379 content_deltify(prior, &rid, 1, 0);
2380 }
2381 if( nWiki<=0 ){
2382 zPrefix = "Deleted";
2383 }else if( !prior ){
2384 zPrefix = "Added";
2385 }else{
2386 zPrefix = "Changes to";
2387 }
2388 switch( wiki_page_type(p->zWikiTitle) ){
2389 case WIKITYPE_CHECKIN: {
2390 zComment = mprintf("%s wiki for check-in [%S]", zPrefix,
2391 p->zWikiTitle+8);
2392 break;
2393 }
2394 case WIKITYPE_BRANCH: {
2395 zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]",
2396 zPrefix, p->zWikiTitle+7, p->zWikiTitle+7);
2397 break;
2398 }
2399 case WIKITYPE_TAG: {
2400 zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]",
2401 zPrefix, p->zWikiTitle+4, p->zWikiTitle+4);
2402 break;
2403 }
2404 default: {
2405 zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle);
2406 break;
2407 }
2408 }
2409 search_doc_touch('w',rid,p->zWikiTitle);
2410 if( manifest_crosslink_busy ){
2411 add_pending_crosslink('w',p->zWikiTitle);
2412 }else{
2413 backlink_wiki_refresh(p->zWikiTitle);
2414 }
2415 db_multi_exec(
2416 "REPLACE INTO event(type,mtime,objid,user,comment,"
2417 " bgcolor,euser,ecomment)"
2418 "VALUES('w',%.17g,%d,%Q,%Q,"
2419 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
2420 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
2421 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
2422 p->rDate, rid, p->zUser, zComment,
2423 TAG_BGCOLOR, rid,
2424 TAG_USER, rid,
2425 TAG_COMMENT, rid
2426 );
2427 fossil_free(zComment);
2428 }
2429 if( p->type==CFTYPE_EVENT ){
2430 char *zTag = mprintf("event-%s", p->zEventId);
2431 int tagid = tag_findid(zTag, 1);
2432 int prior, subsequent;
2433
--- src/manifest.c
+++ src/manifest.c
@@ -2358,14 +2358,14 @@
2358 }
2359 if( p->type==CFTYPE_WIKI ){
2360 char *zTag = mprintf("wiki-%s", p->zWikiTitle);
2361 int tagid = tag_findid(zTag, 1);
2362 int prior;
2363 char cPrefix;
 
2364 int nWiki;
2365 char zLength[40];
2366
2367 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2368 nWiki = strlen(p->zWiki);
2369 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
2370 tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
2371 fossil_free(zTag);
@@ -2377,56 +2377,27 @@
2377 );
2378 if( prior ){
2379 content_deltify(prior, &rid, 1, 0);
2380 }
2381 if( nWiki<=0 ){
2382 cPrefix = '-';
2383 }else if( !prior ){
2384 cPrefix = '+';
2385 }else{
2386 cPrefix = ':';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2387 }
2388 search_doc_touch('w',rid,p->zWikiTitle);
2389 if( manifest_crosslink_busy ){
2390 add_pending_crosslink('w',p->zWikiTitle);
2391 }else{
2392 backlink_wiki_refresh(p->zWikiTitle);
2393 }
2394 db_multi_exec(
2395 "REPLACE INTO event(type,mtime,objid,user,comment)"
2396 "VALUES('w',%.17g,%d,%Q,'%c%q');",
2397 p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle
 
 
 
 
 
 
 
2398 );
 
2399 }
2400 if( p->type==CFTYPE_EVENT ){
2401 char *zTag = mprintf("event-%s", p->zEventId);
2402 int tagid = tag_findid(zTag, 1);
2403 int prior, subsequent;
2404
+1 -1
--- src/markdown.c
+++ src/markdown.c
@@ -514,11 +514,11 @@
514514
}
515515
516516
517517
/* find_emph_char -- looks for the next emph char, skipping other constructs */
518518
static size_t find_emph_char(char *data, size_t size, char c){
519
- size_t i = 1;
519
+ size_t i = data[0]!='`';
520520
521521
while( i<size ){
522522
while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; }
523523
if( i>=size ) return 0;
524524
525525
--- src/markdown.c
+++ src/markdown.c
@@ -514,11 +514,11 @@
514 }
515
516
517 /* find_emph_char -- looks for the next emph char, skipping other constructs */
518 static size_t find_emph_char(char *data, size_t size, char c){
519 size_t i = 1;
520
521 while( i<size ){
522 while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; }
523 if( i>=size ) return 0;
524
525
--- src/markdown.c
+++ src/markdown.c
@@ -514,11 +514,11 @@
514 }
515
516
517 /* find_emph_char -- looks for the next emph char, skipping other constructs */
518 static size_t find_emph_char(char *data, size_t size, char c){
519 size_t i = data[0]!='`';
520
521 while( i<size ){
522 while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; }
523 if( i>=size ) return 0;
524
525
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -347,11 +347,12 @@
347347
int pikFlags = PIKCHR_PROCESS_NONCE
348348
| PIKCHR_PROCESS_DIV
349349
| PIKCHR_PROCESS_SRC
350350
| PIKCHR_PROCESS_ERR_PRE;
351351
Blob bSrc = empty_blob;
352
- const char *zFgColor;
352
+ const char *zPikVar;
353
+ double rPikVar;
353354
354355
while( nArg>0 ){
355356
int i;
356357
for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
357358
if( i==6 && strncmp(zArg, "center", 6)==0 ){
@@ -374,13 +375,27 @@
374375
nArg -= i;
375376
}
376377
if( skin_detail_boolean("white-foreground") ){
377378
pikFlags |= 0x02; /* PIKCHR_DARK_MODE */
378379
}
379
- zFgColor = skin_detail("pikchr-foreground");
380
- if( zFgColor && zFgColor[0] ){
381
- blob_appendf(&bSrc, "fgcolor = %s\n", zFgColor);
380
+ zPikVar = skin_detail("pikchr-foreground");
381
+ if( zPikVar && zPikVar[0] ){
382
+ blob_appendf(&bSrc, "fgcolor = %s\n", zPikVar);
383
+ }
384
+ zPikVar = skin_detail("pikchr-scale");
385
+ if( zPikVar
386
+ && (rPikVar = atof(zPikVar))>=0.1
387
+ && rPikVar<10.0
388
+ ){
389
+ blob_appendf(&bSrc, "scale = %.13g\n", rPikVar);
390
+ }
391
+ zPikVar = skin_detail("pikchr-fontscale");
392
+ if( zPikVar
393
+ && (rPikVar = atof(zPikVar))>=0.1
394
+ && rPikVar<10.0
395
+ ){
396
+ blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
382397
}
383398
blob_append(&bSrc, zSrc, nSrc)
384399
/*have to dup input to ensure a NUL-terminated source string */;
385400
pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
386401
blob_reset(&bSrc);
387402
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -347,11 +347,12 @@
347 int pikFlags = PIKCHR_PROCESS_NONCE
348 | PIKCHR_PROCESS_DIV
349 | PIKCHR_PROCESS_SRC
350 | PIKCHR_PROCESS_ERR_PRE;
351 Blob bSrc = empty_blob;
352 const char *zFgColor;
 
353
354 while( nArg>0 ){
355 int i;
356 for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
357 if( i==6 && strncmp(zArg, "center", 6)==0 ){
@@ -374,13 +375,27 @@
374 nArg -= i;
375 }
376 if( skin_detail_boolean("white-foreground") ){
377 pikFlags |= 0x02; /* PIKCHR_DARK_MODE */
378 }
379 zFgColor = skin_detail("pikchr-foreground");
380 if( zFgColor && zFgColor[0] ){
381 blob_appendf(&bSrc, "fgcolor = %s\n", zFgColor);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382 }
383 blob_append(&bSrc, zSrc, nSrc)
384 /*have to dup input to ensure a NUL-terminated source string */;
385 pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
386 blob_reset(&bSrc);
387
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -347,11 +347,12 @@
347 int pikFlags = PIKCHR_PROCESS_NONCE
348 | PIKCHR_PROCESS_DIV
349 | PIKCHR_PROCESS_SRC
350 | PIKCHR_PROCESS_ERR_PRE;
351 Blob bSrc = empty_blob;
352 const char *zPikVar;
353 double rPikVar;
354
355 while( nArg>0 ){
356 int i;
357 for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
358 if( i==6 && strncmp(zArg, "center", 6)==0 ){
@@ -374,13 +375,27 @@
375 nArg -= i;
376 }
377 if( skin_detail_boolean("white-foreground") ){
378 pikFlags |= 0x02; /* PIKCHR_DARK_MODE */
379 }
380 zPikVar = skin_detail("pikchr-foreground");
381 if( zPikVar && zPikVar[0] ){
382 blob_appendf(&bSrc, "fgcolor = %s\n", zPikVar);
383 }
384 zPikVar = skin_detail("pikchr-scale");
385 if( zPikVar
386 && (rPikVar = atof(zPikVar))>=0.1
387 && rPikVar<10.0
388 ){
389 blob_appendf(&bSrc, "scale = %.13g\n", rPikVar);
390 }
391 zPikVar = skin_detail("pikchr-fontscale");
392 if( zPikVar
393 && (rPikVar = atof(zPikVar))>=0.1
394 && rPikVar<10.0
395 ){
396 blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
397 }
398 blob_append(&bSrc, zSrc, nSrc)
399 /*have to dup input to ensure a NUL-terminated source string */;
400 pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
401 blob_reset(&bSrc);
402
+45 -15
--- src/pikchr.c
+++ src/pikchr.c
@@ -3756,11 +3756,11 @@
37563756
pik_append_arc(p, rad, rad, x0, y2);
37573757
if( y2>y1 ) pik_append_xy(p, "L", x0, y1);
37583758
pik_append_arc(p, rad, rad, x1, y0);
37593759
pik_append(p,"Z\" ",-1);
37603760
}
3761
- pik_append_style(p,pObj,1);
3761
+ pik_append_style(p,pObj,3);
37623762
pik_append(p,"\" />\n", -1);
37633763
}
37643764
pik_append_txt(p, pObj, 0);
37653765
}
37663766
@@ -3818,11 +3818,11 @@
38183818
PPoint pt = pObj->ptAt;
38193819
if( pObj->sw>0.0 ){
38203820
pik_append_x(p,"<circle cx=\"", pt.x, "\"");
38213821
pik_append_y(p," cy=\"", pt.y, "\"");
38223822
pik_append_dis(p," r=\"", r, "\" ");
3823
- pik_append_style(p,pObj,1);
3823
+ pik_append_style(p,pObj,3);
38243824
pik_append(p,"\" />\n", -1);
38253825
}
38263826
pik_append_txt(p, pObj, 0);
38273827
}
38283828
@@ -3832,11 +3832,11 @@
38323832
pObj->h = pik_value(p, "cylht",5,0);
38333833
pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
38343834
}
38353835
static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){
38363836
if( w>0 ) pObj->w = w;
3837
- if( h>0 ) pObj->h = h + 4*pObj->rad + pObj->sw;
3837
+ if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw;
38383838
UNUSED_PARAMETER(p);
38393839
}
38403840
static void cylinderRender(Pik *p, PObj *pObj){
38413841
PNum w2 = 0.5*pObj->w;
38423842
PNum h2 = 0.5*pObj->h;
@@ -3848,11 +3848,11 @@
38483848
pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad);
38493849
pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad);
38503850
pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad);
38513851
pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad);
38523852
pik_append(p,"\" ",-1);
3853
- pik_append_style(p,pObj,1);
3853
+ pik_append_style(p,pObj,3);
38543854
pik_append(p,"\" />\n", -1);
38553855
}
38563856
pik_append_txt(p, pObj, 0);
38573857
}
38583858
static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){
@@ -3910,11 +3910,11 @@
39103910
PPoint pt = pObj->ptAt;
39113911
if( pObj->sw>0.0 ){
39123912
pik_append_x(p,"<circle cx=\"", pt.x, "\"");
39133913
pik_append_y(p," cy=\"", pt.y, "\"");
39143914
pik_append_dis(p," r=\"", r, "\"");
3915
- pik_append_style(p,pObj,1);
3915
+ pik_append_style(p,pObj,2);
39163916
pik_append(p,"\" />\n", -1);
39173917
}
39183918
pik_append_txt(p, pObj, 0);
39193919
}
39203920
@@ -3969,11 +3969,11 @@
39693969
if( pObj->sw>0.0 ){
39703970
pik_append_x(p,"<ellipse cx=\"", pt.x, "\"");
39713971
pik_append_y(p," cy=\"", pt.y, "\"");
39723972
pik_append_dis(p," rx=\"", w/2.0, "\"");
39733973
pik_append_dis(p," ry=\"", h/2.0, "\" ");
3974
- pik_append_style(p,pObj,1);
3974
+ pik_append_style(p,pObj,3);
39753975
pik_append(p,"\" />\n", -1);
39763976
}
39773977
pik_append_txt(p, pObj, 0);
39783978
}
39793979
@@ -4081,11 +4081,11 @@
40814081
pik_append(p,"Z",1);
40824082
}else{
40834083
pObj->fill = -1.0;
40844084
}
40854085
pik_append(p,"\" ",-1);
4086
- pik_append_style(p,pObj,pObj->bClose);
4086
+ pik_append_style(p,pObj,pObj->bClose?3:0);
40874087
pik_append(p,"\" />\n", -1);
40884088
}
40894089
pik_append_txt(p, pObj, 0);
40904090
}
40914091
@@ -4181,11 +4181,11 @@
41814181
pik_append(p,"Z",1);
41824182
}else{
41834183
pObj->fill = -1.0;
41844184
}
41854185
pik_append(p,"\" ",-1);
4186
- pik_append_style(p,pObj,pObj->bClose);
4186
+ pik_append_style(p,pObj,pObj->bClose?3:0);
41874187
pik_append(p,"\" />\n", -1);
41884188
}
41894189
static void splineRender(Pik *p, PObj *pObj){
41904190
if( pObj->sw>0.0 ){
41914191
int n = pObj->nPath;
@@ -4569,10 +4569,13 @@
45694569
pik_append(p, buf, -1);
45704570
}
45714571
45724572
/*
45734573
** Invert the RGB color so that it is appropriate for dark mode.
4574
+** Variable x hold the initial color. The color is intended for use
4575
+** as a background color if isBg is true, and as a foreground color
4576
+** if isBg is false.
45744577
*/
45754578
static int pik_color_to_dark_mode(int x, int isBg){
45764579
int r, g, b;
45774580
int mn, mx;
45784581
x = 0xffffff - x;
@@ -4634,10 +4637,18 @@
46344637
char buf[200];
46354638
snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2);
46364639
buf[sizeof(buf)-1] = 0;
46374640
pik_append(p, buf, -1);
46384641
}
4642
+
4643
+/* Append a color specification to the output.
4644
+**
4645
+** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that
4646
+** the color is intended for use as a background color if true, or as a
4647
+** foreground color if false. The distinction only matters for color
4648
+** inversions in PIKCHR_DARK_MODE.
4649
+*/
46394650
static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){
46404651
char buf[200];
46414652
int x = (int)v;
46424653
int r, g, b;
46434654
if( x==0 && p->fgcolor>0 && !bg ){
@@ -4668,25 +4679,39 @@
46684679
pik_append(p, buf, -1);
46694680
}
46704681
46714682
/* Append a style="..." text. But, leave the quote unterminated, in case
46724683
** the caller wants to add some more.
4684
+**
4685
+** eFill is non-zero to fill in the background, or 0 if no fill should
4686
+** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr()
4687
+** for cases when pObj->fill==pObj->color
4688
+**
4689
+** 1 fill is background, and color is foreground.
4690
+** 2 fill and color are both foreground. (Used by "dot" objects)
4691
+** 3 fill and color are both background. (Used by most other objs)
46734692
*/
4674
-static void pik_append_style(Pik *p, PObj *pObj, int bFill){
4693
+static void pik_append_style(Pik *p, PObj *pObj, int eFill){
4694
+ int clrIsBg = 0;
46754695
pik_append(p, " style=\"", -1);
4676
- if( pObj->fill>=0 && bFill ){
4677
- pik_append_clr(p, "fill:", pObj->fill, ";",1);
4696
+ if( pObj->fill>=0 && eFill ){
4697
+ int fillIsBg = 1;
4698
+ if( pObj->fill==pObj->color ){
4699
+ if( eFill==2 ) fillIsBg = 0;
4700
+ if( eFill==3 ) clrIsBg = 1;
4701
+ }
4702
+ pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg);
46784703
}else{
46794704
pik_append(p,"fill:none;",-1);
46804705
}
46814706
if( pObj->sw>0.0 && pObj->color>=0.0 ){
46824707
PNum sw = pObj->sw;
46834708
pik_append_dis(p, "stroke-width:", sw, ";");
46844709
if( pObj->nPath>2 && pObj->rad<=pObj->sw ){
46854710
pik_append(p, "stroke-linejoin:round;", -1);
46864711
}
4687
- pik_append_clr(p, "stroke:",pObj->color,";",0);
4712
+ pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg);
46884713
if( pObj->dotted>0.0 ){
46894714
PNum v = pObj->dotted;
46904715
if( sw<2.1/p->rScale ) sw = 2.1/p->rScale;
46914716
pik_append_dis(p,"stroke-dasharray:",sw,"");
46924717
pik_append_dis(p,",",v,";");
@@ -4802,10 +4827,11 @@
48024827
PNum ha2 = 0.0; /* Height of the top row of text */
48034828
PNum ha1 = 0.0; /* Height of the second "above" row */
48044829
PNum hc = 0.0; /* Height of the center row */
48054830
PNum hb1 = 0.0; /* Height of the first "below" row of text */
48064831
PNum hb2 = 0.0; /* Height of the second "below" row */
4832
+ PNum yBase = 0.0;
48074833
int n, i, nz;
48084834
PNum x, y, orig_y, s;
48094835
const char *z;
48104836
PToken *aTxt;
48114837
unsigned allMask = 0;
@@ -4815,11 +4841,15 @@
48154841
aTxt = pObj->aTxt;
48164842
n = pObj->nTxt;
48174843
pik_txt_vertical_layout(pObj);
48184844
x = pObj->ptAt.x;
48194845
for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode;
4820
- if( pObj->type->isLine ) hc = pObj->sw*1.5;
4846
+ if( pObj->type->isLine ){
4847
+ hc = pObj->sw*1.5;
4848
+ }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){
4849
+ yBase = -0.75*pObj->rad;
4850
+ }
48214851
if( allMask & TP_CENTER ){
48224852
for(i=0; i<n; i++){
48234853
if( pObj->aTxt[i].eCode & TP_CENTER ){
48244854
s = pik_font_scale(pObj->aTxt+i);
48254855
if( hc<s*p->charHeight ) hc = s*p->charHeight;
@@ -4866,11 +4896,11 @@
48664896
for(i=0; i<n; i++){
48674897
PToken *t = &aTxt[i];
48684898
PNum xtraFontScale = pik_font_scale(t);
48694899
PNum nx = 0;
48704900
orig_y = pObj->ptAt.y;
4871
- y = 0;
4901
+ y = yBase;
48724902
if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2;
48734903
if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1;
48744904
if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1;
48754905
if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
48764906
if( t->eCode & TP_LJUST ) nx -= jw;
@@ -7852,6 +7882,6 @@
78527882
78537883
78547884
#endif /* PIKCHR_TCL */
78557885
78567886
7857
-#line 7882 "pikchr.c"
7887
+#line 7912 "pikchr.c"
78587888
--- src/pikchr.c
+++ src/pikchr.c
@@ -3756,11 +3756,11 @@
3756 pik_append_arc(p, rad, rad, x0, y2);
3757 if( y2>y1 ) pik_append_xy(p, "L", x0, y1);
3758 pik_append_arc(p, rad, rad, x1, y0);
3759 pik_append(p,"Z\" ",-1);
3760 }
3761 pik_append_style(p,pObj,1);
3762 pik_append(p,"\" />\n", -1);
3763 }
3764 pik_append_txt(p, pObj, 0);
3765 }
3766
@@ -3818,11 +3818,11 @@
3818 PPoint pt = pObj->ptAt;
3819 if( pObj->sw>0.0 ){
3820 pik_append_x(p,"<circle cx=\"", pt.x, "\"");
3821 pik_append_y(p," cy=\"", pt.y, "\"");
3822 pik_append_dis(p," r=\"", r, "\" ");
3823 pik_append_style(p,pObj,1);
3824 pik_append(p,"\" />\n", -1);
3825 }
3826 pik_append_txt(p, pObj, 0);
3827 }
3828
@@ -3832,11 +3832,11 @@
3832 pObj->h = pik_value(p, "cylht",5,0);
3833 pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
3834 }
3835 static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){
3836 if( w>0 ) pObj->w = w;
3837 if( h>0 ) pObj->h = h + 4*pObj->rad + pObj->sw;
3838 UNUSED_PARAMETER(p);
3839 }
3840 static void cylinderRender(Pik *p, PObj *pObj){
3841 PNum w2 = 0.5*pObj->w;
3842 PNum h2 = 0.5*pObj->h;
@@ -3848,11 +3848,11 @@
3848 pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad);
3849 pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad);
3850 pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad);
3851 pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad);
3852 pik_append(p,"\" ",-1);
3853 pik_append_style(p,pObj,1);
3854 pik_append(p,"\" />\n", -1);
3855 }
3856 pik_append_txt(p, pObj, 0);
3857 }
3858 static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){
@@ -3910,11 +3910,11 @@
3910 PPoint pt = pObj->ptAt;
3911 if( pObj->sw>0.0 ){
3912 pik_append_x(p,"<circle cx=\"", pt.x, "\"");
3913 pik_append_y(p," cy=\"", pt.y, "\"");
3914 pik_append_dis(p," r=\"", r, "\"");
3915 pik_append_style(p,pObj,1);
3916 pik_append(p,"\" />\n", -1);
3917 }
3918 pik_append_txt(p, pObj, 0);
3919 }
3920
@@ -3969,11 +3969,11 @@
3969 if( pObj->sw>0.0 ){
3970 pik_append_x(p,"<ellipse cx=\"", pt.x, "\"");
3971 pik_append_y(p," cy=\"", pt.y, "\"");
3972 pik_append_dis(p," rx=\"", w/2.0, "\"");
3973 pik_append_dis(p," ry=\"", h/2.0, "\" ");
3974 pik_append_style(p,pObj,1);
3975 pik_append(p,"\" />\n", -1);
3976 }
3977 pik_append_txt(p, pObj, 0);
3978 }
3979
@@ -4081,11 +4081,11 @@
4081 pik_append(p,"Z",1);
4082 }else{
4083 pObj->fill = -1.0;
4084 }
4085 pik_append(p,"\" ",-1);
4086 pik_append_style(p,pObj,pObj->bClose);
4087 pik_append(p,"\" />\n", -1);
4088 }
4089 pik_append_txt(p, pObj, 0);
4090 }
4091
@@ -4181,11 +4181,11 @@
4181 pik_append(p,"Z",1);
4182 }else{
4183 pObj->fill = -1.0;
4184 }
4185 pik_append(p,"\" ",-1);
4186 pik_append_style(p,pObj,pObj->bClose);
4187 pik_append(p,"\" />\n", -1);
4188 }
4189 static void splineRender(Pik *p, PObj *pObj){
4190 if( pObj->sw>0.0 ){
4191 int n = pObj->nPath;
@@ -4569,10 +4569,13 @@
4569 pik_append(p, buf, -1);
4570 }
4571
4572 /*
4573 ** Invert the RGB color so that it is appropriate for dark mode.
 
 
 
4574 */
4575 static int pik_color_to_dark_mode(int x, int isBg){
4576 int r, g, b;
4577 int mn, mx;
4578 x = 0xffffff - x;
@@ -4634,10 +4637,18 @@
4634 char buf[200];
4635 snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2);
4636 buf[sizeof(buf)-1] = 0;
4637 pik_append(p, buf, -1);
4638 }
 
 
 
 
 
 
 
 
4639 static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){
4640 char buf[200];
4641 int x = (int)v;
4642 int r, g, b;
4643 if( x==0 && p->fgcolor>0 && !bg ){
@@ -4668,25 +4679,39 @@
4668 pik_append(p, buf, -1);
4669 }
4670
4671 /* Append a style="..." text. But, leave the quote unterminated, in case
4672 ** the caller wants to add some more.
 
 
 
 
 
 
 
 
4673 */
4674 static void pik_append_style(Pik *p, PObj *pObj, int bFill){
 
4675 pik_append(p, " style=\"", -1);
4676 if( pObj->fill>=0 && bFill ){
4677 pik_append_clr(p, "fill:", pObj->fill, ";",1);
 
 
 
 
 
4678 }else{
4679 pik_append(p,"fill:none;",-1);
4680 }
4681 if( pObj->sw>0.0 && pObj->color>=0.0 ){
4682 PNum sw = pObj->sw;
4683 pik_append_dis(p, "stroke-width:", sw, ";");
4684 if( pObj->nPath>2 && pObj->rad<=pObj->sw ){
4685 pik_append(p, "stroke-linejoin:round;", -1);
4686 }
4687 pik_append_clr(p, "stroke:",pObj->color,";",0);
4688 if( pObj->dotted>0.0 ){
4689 PNum v = pObj->dotted;
4690 if( sw<2.1/p->rScale ) sw = 2.1/p->rScale;
4691 pik_append_dis(p,"stroke-dasharray:",sw,"");
4692 pik_append_dis(p,",",v,";");
@@ -4802,10 +4827,11 @@
4802 PNum ha2 = 0.0; /* Height of the top row of text */
4803 PNum ha1 = 0.0; /* Height of the second "above" row */
4804 PNum hc = 0.0; /* Height of the center row */
4805 PNum hb1 = 0.0; /* Height of the first "below" row of text */
4806 PNum hb2 = 0.0; /* Height of the second "below" row */
 
4807 int n, i, nz;
4808 PNum x, y, orig_y, s;
4809 const char *z;
4810 PToken *aTxt;
4811 unsigned allMask = 0;
@@ -4815,11 +4841,15 @@
4815 aTxt = pObj->aTxt;
4816 n = pObj->nTxt;
4817 pik_txt_vertical_layout(pObj);
4818 x = pObj->ptAt.x;
4819 for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode;
4820 if( pObj->type->isLine ) hc = pObj->sw*1.5;
 
 
 
 
4821 if( allMask & TP_CENTER ){
4822 for(i=0; i<n; i++){
4823 if( pObj->aTxt[i].eCode & TP_CENTER ){
4824 s = pik_font_scale(pObj->aTxt+i);
4825 if( hc<s*p->charHeight ) hc = s*p->charHeight;
@@ -4866,11 +4896,11 @@
4866 for(i=0; i<n; i++){
4867 PToken *t = &aTxt[i];
4868 PNum xtraFontScale = pik_font_scale(t);
4869 PNum nx = 0;
4870 orig_y = pObj->ptAt.y;
4871 y = 0;
4872 if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2;
4873 if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1;
4874 if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1;
4875 if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
4876 if( t->eCode & TP_LJUST ) nx -= jw;
@@ -7852,6 +7882,6 @@
7852
7853
7854 #endif /* PIKCHR_TCL */
7855
7856
7857 #line 7882 "pikchr.c"
7858
--- src/pikchr.c
+++ src/pikchr.c
@@ -3756,11 +3756,11 @@
3756 pik_append_arc(p, rad, rad, x0, y2);
3757 if( y2>y1 ) pik_append_xy(p, "L", x0, y1);
3758 pik_append_arc(p, rad, rad, x1, y0);
3759 pik_append(p,"Z\" ",-1);
3760 }
3761 pik_append_style(p,pObj,3);
3762 pik_append(p,"\" />\n", -1);
3763 }
3764 pik_append_txt(p, pObj, 0);
3765 }
3766
@@ -3818,11 +3818,11 @@
3818 PPoint pt = pObj->ptAt;
3819 if( pObj->sw>0.0 ){
3820 pik_append_x(p,"<circle cx=\"", pt.x, "\"");
3821 pik_append_y(p," cy=\"", pt.y, "\"");
3822 pik_append_dis(p," r=\"", r, "\" ");
3823 pik_append_style(p,pObj,3);
3824 pik_append(p,"\" />\n", -1);
3825 }
3826 pik_append_txt(p, pObj, 0);
3827 }
3828
@@ -3832,11 +3832,11 @@
3832 pObj->h = pik_value(p, "cylht",5,0);
3833 pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
3834 }
3835 static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){
3836 if( w>0 ) pObj->w = w;
3837 if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw;
3838 UNUSED_PARAMETER(p);
3839 }
3840 static void cylinderRender(Pik *p, PObj *pObj){
3841 PNum w2 = 0.5*pObj->w;
3842 PNum h2 = 0.5*pObj->h;
@@ -3848,11 +3848,11 @@
3848 pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad);
3849 pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad);
3850 pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad);
3851 pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad);
3852 pik_append(p,"\" ",-1);
3853 pik_append_style(p,pObj,3);
3854 pik_append(p,"\" />\n", -1);
3855 }
3856 pik_append_txt(p, pObj, 0);
3857 }
3858 static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){
@@ -3910,11 +3910,11 @@
3910 PPoint pt = pObj->ptAt;
3911 if( pObj->sw>0.0 ){
3912 pik_append_x(p,"<circle cx=\"", pt.x, "\"");
3913 pik_append_y(p," cy=\"", pt.y, "\"");
3914 pik_append_dis(p," r=\"", r, "\"");
3915 pik_append_style(p,pObj,2);
3916 pik_append(p,"\" />\n", -1);
3917 }
3918 pik_append_txt(p, pObj, 0);
3919 }
3920
@@ -3969,11 +3969,11 @@
3969 if( pObj->sw>0.0 ){
3970 pik_append_x(p,"<ellipse cx=\"", pt.x, "\"");
3971 pik_append_y(p," cy=\"", pt.y, "\"");
3972 pik_append_dis(p," rx=\"", w/2.0, "\"");
3973 pik_append_dis(p," ry=\"", h/2.0, "\" ");
3974 pik_append_style(p,pObj,3);
3975 pik_append(p,"\" />\n", -1);
3976 }
3977 pik_append_txt(p, pObj, 0);
3978 }
3979
@@ -4081,11 +4081,11 @@
4081 pik_append(p,"Z",1);
4082 }else{
4083 pObj->fill = -1.0;
4084 }
4085 pik_append(p,"\" ",-1);
4086 pik_append_style(p,pObj,pObj->bClose?3:0);
4087 pik_append(p,"\" />\n", -1);
4088 }
4089 pik_append_txt(p, pObj, 0);
4090 }
4091
@@ -4181,11 +4181,11 @@
4181 pik_append(p,"Z",1);
4182 }else{
4183 pObj->fill = -1.0;
4184 }
4185 pik_append(p,"\" ",-1);
4186 pik_append_style(p,pObj,pObj->bClose?3:0);
4187 pik_append(p,"\" />\n", -1);
4188 }
4189 static void splineRender(Pik *p, PObj *pObj){
4190 if( pObj->sw>0.0 ){
4191 int n = pObj->nPath;
@@ -4569,10 +4569,13 @@
4569 pik_append(p, buf, -1);
4570 }
4571
4572 /*
4573 ** Invert the RGB color so that it is appropriate for dark mode.
4574 ** Variable x hold the initial color. The color is intended for use
4575 ** as a background color if isBg is true, and as a foreground color
4576 ** if isBg is false.
4577 */
4578 static int pik_color_to_dark_mode(int x, int isBg){
4579 int r, g, b;
4580 int mn, mx;
4581 x = 0xffffff - x;
@@ -4634,10 +4637,18 @@
4637 char buf[200];
4638 snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2);
4639 buf[sizeof(buf)-1] = 0;
4640 pik_append(p, buf, -1);
4641 }
4642
4643 /* Append a color specification to the output.
4644 **
4645 ** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that
4646 ** the color is intended for use as a background color if true, or as a
4647 ** foreground color if false. The distinction only matters for color
4648 ** inversions in PIKCHR_DARK_MODE.
4649 */
4650 static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){
4651 char buf[200];
4652 int x = (int)v;
4653 int r, g, b;
4654 if( x==0 && p->fgcolor>0 && !bg ){
@@ -4668,25 +4679,39 @@
4679 pik_append(p, buf, -1);
4680 }
4681
4682 /* Append a style="..." text. But, leave the quote unterminated, in case
4683 ** the caller wants to add some more.
4684 **
4685 ** eFill is non-zero to fill in the background, or 0 if no fill should
4686 ** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr()
4687 ** for cases when pObj->fill==pObj->color
4688 **
4689 ** 1 fill is background, and color is foreground.
4690 ** 2 fill and color are both foreground. (Used by "dot" objects)
4691 ** 3 fill and color are both background. (Used by most other objs)
4692 */
4693 static void pik_append_style(Pik *p, PObj *pObj, int eFill){
4694 int clrIsBg = 0;
4695 pik_append(p, " style=\"", -1);
4696 if( pObj->fill>=0 && eFill ){
4697 int fillIsBg = 1;
4698 if( pObj->fill==pObj->color ){
4699 if( eFill==2 ) fillIsBg = 0;
4700 if( eFill==3 ) clrIsBg = 1;
4701 }
4702 pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg);
4703 }else{
4704 pik_append(p,"fill:none;",-1);
4705 }
4706 if( pObj->sw>0.0 && pObj->color>=0.0 ){
4707 PNum sw = pObj->sw;
4708 pik_append_dis(p, "stroke-width:", sw, ";");
4709 if( pObj->nPath>2 && pObj->rad<=pObj->sw ){
4710 pik_append(p, "stroke-linejoin:round;", -1);
4711 }
4712 pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg);
4713 if( pObj->dotted>0.0 ){
4714 PNum v = pObj->dotted;
4715 if( sw<2.1/p->rScale ) sw = 2.1/p->rScale;
4716 pik_append_dis(p,"stroke-dasharray:",sw,"");
4717 pik_append_dis(p,",",v,";");
@@ -4802,10 +4827,11 @@
4827 PNum ha2 = 0.0; /* Height of the top row of text */
4828 PNum ha1 = 0.0; /* Height of the second "above" row */
4829 PNum hc = 0.0; /* Height of the center row */
4830 PNum hb1 = 0.0; /* Height of the first "below" row of text */
4831 PNum hb2 = 0.0; /* Height of the second "below" row */
4832 PNum yBase = 0.0;
4833 int n, i, nz;
4834 PNum x, y, orig_y, s;
4835 const char *z;
4836 PToken *aTxt;
4837 unsigned allMask = 0;
@@ -4815,11 +4841,15 @@
4841 aTxt = pObj->aTxt;
4842 n = pObj->nTxt;
4843 pik_txt_vertical_layout(pObj);
4844 x = pObj->ptAt.x;
4845 for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode;
4846 if( pObj->type->isLine ){
4847 hc = pObj->sw*1.5;
4848 }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){
4849 yBase = -0.75*pObj->rad;
4850 }
4851 if( allMask & TP_CENTER ){
4852 for(i=0; i<n; i++){
4853 if( pObj->aTxt[i].eCode & TP_CENTER ){
4854 s = pik_font_scale(pObj->aTxt+i);
4855 if( hc<s*p->charHeight ) hc = s*p->charHeight;
@@ -4866,11 +4896,11 @@
4896 for(i=0; i<n; i++){
4897 PToken *t = &aTxt[i];
4898 PNum xtraFontScale = pik_font_scale(t);
4899 PNum nx = 0;
4900 orig_y = pObj->ptAt.y;
4901 y = yBase;
4902 if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2;
4903 if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1;
4904 if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1;
4905 if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
4906 if( t->eCode & TP_LJUST ) nx -= jw;
@@ -7852,6 +7882,6 @@
7882
7883
7884 #endif /* PIKCHR_TCL */
7885
7886
7887 #line 7912 "pikchr.c"
7888
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -335,11 +335,11 @@
335335
CX("body.pikchrshow .v-align-middle{"
336336
"vertical-align: middle"
337337
"}\n");
338338
CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n");
339339
} CX("</style>");
340
- CX("<div>Input pikchr code and tap Preview or Ctrl-Enter) to render "
340
+ CX("<div>Input pikchr code and tap Preview (or Ctrl-Enter) to render "
341341
"it:</div>");
342342
CX("<div id='sbs-wrapper'>"); {
343343
CX("<div id='pikchrshow-form'>"); {
344344
CX("<textarea id='content' name='content' rows='15'>"
345345
"%s</textarea>",zContent/*safe-for-%s*/);
@@ -371,11 +371,11 @@
371371
blob_reset(&out);
372372
} CX("</div>"/*#pikchrshow-output*/);
373373
} CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
374374
} CX("</div>"/*sbs-wrapper*/);
375375
builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
376
- "storage", "pikchr", 0);
376
+ "storage", "pikchr", NULL);
377377
builtin_request_js("fossil.page.pikchrshow.js");
378378
builtin_fulfill_js_requests();
379379
style_finish_page("pikchrshow");
380380
}
381381
382382
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -335,11 +335,11 @@
335 CX("body.pikchrshow .v-align-middle{"
336 "vertical-align: middle"
337 "}\n");
338 CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n");
339 } CX("</style>");
340 CX("<div>Input pikchr code and tap Preview or Ctrl-Enter) to render "
341 "it:</div>");
342 CX("<div id='sbs-wrapper'>"); {
343 CX("<div id='pikchrshow-form'>"); {
344 CX("<textarea id='content' name='content' rows='15'>"
345 "%s</textarea>",zContent/*safe-for-%s*/);
@@ -371,11 +371,11 @@
371 blob_reset(&out);
372 } CX("</div>"/*#pikchrshow-output*/);
373 } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
374 } CX("</div>"/*sbs-wrapper*/);
375 builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
376 "storage", "pikchr", 0);
377 builtin_request_js("fossil.page.pikchrshow.js");
378 builtin_fulfill_js_requests();
379 style_finish_page("pikchrshow");
380 }
381
382
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -335,11 +335,11 @@
335 CX("body.pikchrshow .v-align-middle{"
336 "vertical-align: middle"
337 "}\n");
338 CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n");
339 } CX("</style>");
340 CX("<div>Input pikchr code and tap Preview (or Ctrl-Enter) to render "
341 "it:</div>");
342 CX("<div id='sbs-wrapper'>"); {
343 CX("<div id='pikchrshow-form'>"); {
344 CX("<textarea id='content' name='content' rows='15'>"
345 "%s</textarea>",zContent/*safe-for-%s*/);
@@ -371,11 +371,11 @@
371 blob_reset(&out);
372 } CX("</div>"/*#pikchrshow-output*/);
373 } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
374 } CX("</div>"/*sbs-wrapper*/);
375 builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
376 "storage", "pikchr", NULL);
377 builtin_request_js("fossil.page.pikchrshow.js");
378 builtin_fulfill_js_requests();
379 style_finish_page("pikchrshow");
380 }
381
382
+33 -14
--- src/printf.c
+++ src/printf.c
@@ -134,13 +134,17 @@
134134
135135
136136
/*
137137
** The following table is searched linearly, so it is good to put the
138138
** most frequently used conversion types first.
139
+**
140
+** NB: When modifying this table is it vital that you also update the fmtchr[]
141
+** variable to match!!!
139142
*/
140143
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
141144
static const char aPrefix[] = "-x0\000X0";
145
+static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$";
142146
static const et_info fmtinfo[] = {
143147
{ 'd', 10, 1, etRADIX, 0, 0 },
144148
{ 's', 0, 4, etSTRING, 0, 0 },
145149
{ 'g', 0, 1, etGENERIC, 30, 0 },
146150
{ 'z', 0, 6, etDYNSTRING, 0, 0 },
@@ -170,12 +174,26 @@
170174
{ 'n', 0, 0, etSIZE, 0, 0 },
171175
{ '%', 0, 0, etPERCENT, 0, 0 },
172176
{ 'p', 16, 0, etPOINTER, 0, 1 },
173177
{ '/', 0, 0, etPATH, 0, 0 },
174178
{ '$', 0, 0, etSHELLESC, 0, 0 },
179
+ { etERROR, 0,0,0,0,0} /* Must be last */
175180
};
176181
#define etNINFO count(fmtinfo)
182
+
183
+/*
184
+** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
185
+**
186
+** This routine is a defense against programming errors.
187
+*/
188
+void fossil_printf_selfcheck(void){
189
+ int i;
190
+ for(i=0; fmtchr[i]; i++){
191
+ assert( fmtchr[i]==fmtinfo[i].fmttype );
192
+ }
193
+}
194
+
177195
178196
/*
179197
** "*val" is a double such that 0.1 <= *val < 10.0
180198
** Return the ascii code for the leading digit of *val, then
181199
** multiply "*val" by 10.0 to renormalize.
@@ -306,22 +324,24 @@
306324
double rounder; /* Used for rounding floating point values */
307325
etByte flag_dp; /* True if decimal point should be shown */
308326
etByte flag_rtz; /* True if trailing zeros should be removed */
309327
etByte flag_exp; /* True to force display of the exponent */
310328
int nsd; /* Number of significant digits returned */
329
+ char *zFmtLookup;
311330
312331
count = length = 0;
313332
bufpt = 0;
314333
for(; (c=(*fmt))!=0; ++fmt){
315334
if( c!='%' ){
316
- int amt;
317335
bufpt = (char *)fmt;
318
- amt = 1;
319
- while( (c=(*++fmt))!='%' && c!=0 ) amt++;
320
- blob_append(pBlob,bufpt,amt);
321
- count += amt;
322
- if( c==0 ) break;
336
+#if HAVE_STRCHRNUL
337
+ fmt = strchrnul(fmt, '%');
338
+#else
339
+ do{ fmt++; }while( *fmt && *fmt != '%' );
340
+#endif
341
+ blob_append(pBlob, bufpt, (int)(fmt - bufpt));
342
+ if( *fmt==0 ) break;
323343
}
324344
if( (c=(*++fmt))==0 ){
325345
errorflag = 1;
326346
blob_append(pBlob,"%",1);
327347
count++;
@@ -390,18 +410,17 @@
390410
}
391411
}else{
392412
flag_long = flag_longlong = 0;
393413
}
394414
/* Fetch the info entry for the field */
395
- infop = 0;
396
- xtype = etERROR;
397
- for(idx=0; idx<etNINFO; idx++){
398
- if( c==fmtinfo[idx].fmttype ){
399
- infop = &fmtinfo[idx];
400
- xtype = infop->type;
401
- break;
402
- }
415
+ zFmtLookup = strchr(fmtchr,c);
416
+ if( zFmtLookup ){
417
+ infop = &fmtinfo[zFmtLookup-fmtchr];
418
+ xtype = infop->type;
419
+ }else{
420
+ infop = 0;
421
+ xtype = etERROR;
403422
}
404423
zExtra = 0;
405424
406425
/* Limit the precision to prevent overflowing buf[] during conversion */
407426
if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
408427
--- src/printf.c
+++ src/printf.c
@@ -134,13 +134,17 @@
134
135
136 /*
137 ** The following table is searched linearly, so it is good to put the
138 ** most frequently used conversion types first.
 
 
 
139 */
140 static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
141 static const char aPrefix[] = "-x0\000X0";
 
142 static const et_info fmtinfo[] = {
143 { 'd', 10, 1, etRADIX, 0, 0 },
144 { 's', 0, 4, etSTRING, 0, 0 },
145 { 'g', 0, 1, etGENERIC, 30, 0 },
146 { 'z', 0, 6, etDYNSTRING, 0, 0 },
@@ -170,12 +174,26 @@
170 { 'n', 0, 0, etSIZE, 0, 0 },
171 { '%', 0, 0, etPERCENT, 0, 0 },
172 { 'p', 16, 0, etPOINTER, 0, 1 },
173 { '/', 0, 0, etPATH, 0, 0 },
174 { '$', 0, 0, etSHELLESC, 0, 0 },
 
175 };
176 #define etNINFO count(fmtinfo)
 
 
 
 
 
 
 
 
 
 
 
 
 
177
178 /*
179 ** "*val" is a double such that 0.1 <= *val < 10.0
180 ** Return the ascii code for the leading digit of *val, then
181 ** multiply "*val" by 10.0 to renormalize.
@@ -306,22 +324,24 @@
306 double rounder; /* Used for rounding floating point values */
307 etByte flag_dp; /* True if decimal point should be shown */
308 etByte flag_rtz; /* True if trailing zeros should be removed */
309 etByte flag_exp; /* True to force display of the exponent */
310 int nsd; /* Number of significant digits returned */
 
311
312 count = length = 0;
313 bufpt = 0;
314 for(; (c=(*fmt))!=0; ++fmt){
315 if( c!='%' ){
316 int amt;
317 bufpt = (char *)fmt;
318 amt = 1;
319 while( (c=(*++fmt))!='%' && c!=0 ) amt++;
320 blob_append(pBlob,bufpt,amt);
321 count += amt;
322 if( c==0 ) break;
 
 
323 }
324 if( (c=(*++fmt))==0 ){
325 errorflag = 1;
326 blob_append(pBlob,"%",1);
327 count++;
@@ -390,18 +410,17 @@
390 }
391 }else{
392 flag_long = flag_longlong = 0;
393 }
394 /* Fetch the info entry for the field */
395 infop = 0;
396 xtype = etERROR;
397 for(idx=0; idx<etNINFO; idx++){
398 if( c==fmtinfo[idx].fmttype ){
399 infop = &fmtinfo[idx];
400 xtype = infop->type;
401 break;
402 }
403 }
404 zExtra = 0;
405
406 /* Limit the precision to prevent overflowing buf[] during conversion */
407 if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
408
--- src/printf.c
+++ src/printf.c
@@ -134,13 +134,17 @@
134
135
136 /*
137 ** The following table is searched linearly, so it is good to put the
138 ** most frequently used conversion types first.
139 **
140 ** NB: When modifying this table is it vital that you also update the fmtchr[]
141 ** variable to match!!!
142 */
143 static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
144 static const char aPrefix[] = "-x0\000X0";
145 static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$";
146 static const et_info fmtinfo[] = {
147 { 'd', 10, 1, etRADIX, 0, 0 },
148 { 's', 0, 4, etSTRING, 0, 0 },
149 { 'g', 0, 1, etGENERIC, 30, 0 },
150 { 'z', 0, 6, etDYNSTRING, 0, 0 },
@@ -170,12 +174,26 @@
174 { 'n', 0, 0, etSIZE, 0, 0 },
175 { '%', 0, 0, etPERCENT, 0, 0 },
176 { 'p', 16, 0, etPOINTER, 0, 1 },
177 { '/', 0, 0, etPATH, 0, 0 },
178 { '$', 0, 0, etSHELLESC, 0, 0 },
179 { etERROR, 0,0,0,0,0} /* Must be last */
180 };
181 #define etNINFO count(fmtinfo)
182
183 /*
184 ** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
185 **
186 ** This routine is a defense against programming errors.
187 */
188 void fossil_printf_selfcheck(void){
189 int i;
190 for(i=0; fmtchr[i]; i++){
191 assert( fmtchr[i]==fmtinfo[i].fmttype );
192 }
193 }
194
195
196 /*
197 ** "*val" is a double such that 0.1 <= *val < 10.0
198 ** Return the ascii code for the leading digit of *val, then
199 ** multiply "*val" by 10.0 to renormalize.
@@ -306,22 +324,24 @@
324 double rounder; /* Used for rounding floating point values */
325 etByte flag_dp; /* True if decimal point should be shown */
326 etByte flag_rtz; /* True if trailing zeros should be removed */
327 etByte flag_exp; /* True to force display of the exponent */
328 int nsd; /* Number of significant digits returned */
329 char *zFmtLookup;
330
331 count = length = 0;
332 bufpt = 0;
333 for(; (c=(*fmt))!=0; ++fmt){
334 if( c!='%' ){
 
335 bufpt = (char *)fmt;
336 #if HAVE_STRCHRNUL
337 fmt = strchrnul(fmt, '%');
338 #else
339 do{ fmt++; }while( *fmt && *fmt != '%' );
340 #endif
341 blob_append(pBlob, bufpt, (int)(fmt - bufpt));
342 if( *fmt==0 ) break;
343 }
344 if( (c=(*++fmt))==0 ){
345 errorflag = 1;
346 blob_append(pBlob,"%",1);
347 count++;
@@ -390,18 +410,17 @@
410 }
411 }else{
412 flag_long = flag_longlong = 0;
413 }
414 /* Fetch the info entry for the field */
415 zFmtLookup = strchr(fmtchr,c);
416 if( zFmtLookup ){
417 infop = &fmtinfo[zFmtLookup-fmtchr];
418 xtype = infop->type;
419 }else{
420 infop = 0;
421 xtype = etERROR;
 
422 }
423 zExtra = 0;
424
425 /* Limit the precision to prevent overflowing buf[] during conversion */
426 if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
427
+39 -16
--- src/shell.c
+++ src/shell.c
@@ -5420,14 +5420,14 @@
54205420
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
54215421
** result set of queries against generate_series will look like.
54225422
*/
54235423
static int seriesConnect(
54245424
sqlite3 *db,
5425
- void *pAux,
5426
- int argc, const char *const*argv,
5425
+ void *pUnused,
5426
+ int argcUnused, const char *const*argvUnused,
54275427
sqlite3_vtab **ppVtab,
5428
- char **pzErr
5428
+ char **pzErrUnused
54295429
){
54305430
sqlite3_vtab *pNew;
54315431
int rc;
54325432
54335433
/* Column numbers */
@@ -5434,10 +5434,14 @@
54345434
#define SERIES_COLUMN_VALUE 0
54355435
#define SERIES_COLUMN_START 1
54365436
#define SERIES_COLUMN_STOP 2
54375437
#define SERIES_COLUMN_STEP 3
54385438
5439
+ (void)pUnused;
5440
+ (void)argcUnused;
5441
+ (void)argvUnused;
5442
+ (void)pzErrUnused;
54395443
rc = sqlite3_declare_vtab(db,
54405444
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
54415445
if( rc==SQLITE_OK ){
54425446
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
54435447
if( pNew==0 ) return SQLITE_NOMEM;
@@ -5456,12 +5460,13 @@
54565460
}
54575461
54585462
/*
54595463
** Constructor for a new series_cursor object.
54605464
*/
5461
-static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
5465
+static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
54625466
series_cursor *pCur;
5467
+ (void)pUnused;
54635468
pCur = sqlite3_malloc( sizeof(*pCur) );
54645469
if( pCur==0 ) return SQLITE_NOMEM;
54655470
memset(pCur, 0, sizeof(*pCur));
54665471
*ppCursor = &pCur->base;
54675472
return SQLITE_OK;
@@ -5564,15 +5569,16 @@
55645569
** is pointing at the first row, or pointing off the end of the table
55655570
** (so that seriesEof() will return true) if the table is empty.
55665571
*/
55675572
static int seriesFilter(
55685573
sqlite3_vtab_cursor *pVtabCursor,
5569
- int idxNum, const char *idxStr,
5574
+ int idxNum, const char *idxStrUnused,
55705575
int argc, sqlite3_value **argv
55715576
){
55725577
series_cursor *pCur = (series_cursor *)pVtabCursor;
55735578
int i = 0;
5579
+ (void)idxStrUnused;
55745580
if( idxNum & 1 ){
55755581
pCur->mnValue = sqlite3_value_int64(argv[i++]);
55765582
}else{
55775583
pCur->mnValue = 0;
55785584
}
@@ -5625,11 +5631,11 @@
56255631
** (2) stop = $value -- constraint exists
56265632
** (4) step = $value -- constraint exists
56275633
** (8) output in descending order
56285634
*/
56295635
static int seriesBestIndex(
5630
- sqlite3_vtab *tab,
5636
+ sqlite3_vtab *tabUnused,
56315637
sqlite3_index_info *pIdxInfo
56325638
){
56335639
int i, j; /* Loop over constraints */
56345640
int idxNum = 0; /* The query plan bitmask */
56355641
int unusableMask = 0; /* Mask of unusable constraints */
@@ -5639,10 +5645,11 @@
56395645
56405646
/* This implementation assumes that the start, stop, and step columns
56415647
** are the last three columns in the virtual table. */
56425648
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
56435649
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
5650
+ (void)tabUnused;
56445651
aIdx[0] = aIdx[1] = aIdx[2] = -1;
56455652
pConstraint = pIdxInfo->aConstraint;
56465653
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
56475654
int iCol; /* 0 for start, 1 for stop, 2 for step */
56485655
int iMask; /* bitmask for those column */
@@ -5712,10 +5719,14 @@
57125719
0, /* xSync */
57135720
0, /* xCommit */
57145721
0, /* xRollback */
57155722
0, /* xFindMethod */
57165723
0, /* xRename */
5724
+ 0, /* xSavepoint */
5725
+ 0, /* xRelease */
5726
+ 0, /* xRollbackTo */
5727
+ 0 /* xShadowName */
57175728
};
57185729
57195730
#endif /* SQLITE_OMIT_VIRTUALTABLE */
57205731
57215732
#ifdef _WIN32
@@ -14455,14 +14466,15 @@
1445514466
/*
1445614467
** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
1445714468
*/
1445814469
static void shellUSleepFunc(
1445914470
sqlite3_context *context,
14460
- int argc,
14471
+ int argcUnused,
1446114472
sqlite3_value **argv
1446214473
){
1446314474
int sleep = sqlite3_value_int(argv[0]);
14475
+ (void)argcUnused;
1446414476
sqlite3_sleep(sleep/1000);
1446514477
sqlite3_result_int(context, sleep);
1446614478
}
1446714479
1446814480
/*
@@ -20657,12 +20669,15 @@
2065720669
p->in = fopen(sqliterc,"rb");
2065820670
if( p->in ){
2065920671
if( stdin_is_interactive ){
2066020672
utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc);
2066120673
}
20662
- process_input(p);
20674
+ if( process_input(p) && bail_on_error ) exit(1);
2066320675
fclose(p->in);
20676
+ }else if( sqliterc_override!=0 ){
20677
+ utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc);
20678
+ if( bail_on_error ) exit(1);
2066420679
}
2066520680
p->in = inSaved;
2066620681
p->lineno = savedLineno;
2066720682
sqlite3_free(zBuf);
2066820683
}
@@ -21044,10 +21059,12 @@
2104421059
** command, so ignore them */
2104521060
break;
2104621061
#endif
2104721062
}else if( strcmp(z, "-memtrace")==0 ){
2104821063
sqlite3MemTraceActivate(stderr);
21064
+ }else if( strcmp(z,"-bail")==0 ){
21065
+ bail_on_error = 1;
2104921066
}
2105021067
}
2105121068
verify_uninitialized();
2105221069
2105321070
@@ -21190,11 +21207,11 @@
2119021207
** prior to sending the SQL into SQLite. Useful for injecting
2119121208
** crazy bytes in the middle of SQL statements for testing and debugging.
2119221209
*/
2119321210
ShellSetFlag(&data, SHFLG_Backslash);
2119421211
}else if( strcmp(z,"-bail")==0 ){
21195
- bail_on_error = 1;
21212
+ /* No-op. The bail_on_error flag should already be set. */
2119621213
}else if( strcmp(z,"-version")==0 ){
2119721214
printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
2119821215
return 0;
2119921216
}else if( strcmp(z,"-interactive")==0 ){
2120021217
stdin_is_interactive = 1;
@@ -21278,24 +21295,29 @@
2127821295
** the database filename.
2127921296
*/
2128021297
for(i=0; i<nCmd; i++){
2128121298
if( azCmd[i][0]=='.' ){
2128221299
rc = do_meta_command(azCmd[i], &data);
21283
- if( rc ) return rc==2 ? 0 : rc;
21300
+ if( rc ){
21301
+ free(azCmd);
21302
+ return rc==2 ? 0 : rc;
21303
+ }
2128421304
}else{
2128521305
open_db(&data, 0);
2128621306
rc = shell_exec(&data, azCmd[i], &zErrMsg);
21287
- if( zErrMsg!=0 ){
21288
- utf8_printf(stderr,"Error: %s\n", zErrMsg);
21307
+ if( zErrMsg || rc ){
21308
+ if( zErrMsg!=0 ){
21309
+ utf8_printf(stderr,"Error: %s\n", zErrMsg);
21310
+ }else{
21311
+ utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
21312
+ }
21313
+ sqlite3_free(zErrMsg);
21314
+ free(azCmd);
2128921315
return rc!=0 ? rc : 1;
21290
- }else if( rc!=0 ){
21291
- utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
21292
- return rc;
2129321316
}
2129421317
}
2129521318
}
21296
- free(azCmd);
2129721319
}else{
2129821320
/* Run commands received from standard input
2129921321
*/
2130021322
if( stdin_is_interactive ){
2130121323
char *zHome;
@@ -21337,10 +21359,11 @@
2133721359
}else{
2133821360
data.in = stdin;
2133921361
rc = process_input(&data);
2134021362
}
2134121363
}
21364
+ free(azCmd);
2134221365
set_table_name(&data, 0);
2134321366
if( data.db ){
2134421367
session_close_all(&data);
2134521368
close_db(data.db);
2134621369
}
2134721370
--- src/shell.c
+++ src/shell.c
@@ -5420,14 +5420,14 @@
5420 ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
5421 ** result set of queries against generate_series will look like.
5422 */
5423 static int seriesConnect(
5424 sqlite3 *db,
5425 void *pAux,
5426 int argc, const char *const*argv,
5427 sqlite3_vtab **ppVtab,
5428 char **pzErr
5429 ){
5430 sqlite3_vtab *pNew;
5431 int rc;
5432
5433 /* Column numbers */
@@ -5434,10 +5434,14 @@
5434 #define SERIES_COLUMN_VALUE 0
5435 #define SERIES_COLUMN_START 1
5436 #define SERIES_COLUMN_STOP 2
5437 #define SERIES_COLUMN_STEP 3
5438
 
 
 
 
5439 rc = sqlite3_declare_vtab(db,
5440 "CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
5441 if( rc==SQLITE_OK ){
5442 pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
5443 if( pNew==0 ) return SQLITE_NOMEM;
@@ -5456,12 +5460,13 @@
5456 }
5457
5458 /*
5459 ** Constructor for a new series_cursor object.
5460 */
5461 static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
5462 series_cursor *pCur;
 
5463 pCur = sqlite3_malloc( sizeof(*pCur) );
5464 if( pCur==0 ) return SQLITE_NOMEM;
5465 memset(pCur, 0, sizeof(*pCur));
5466 *ppCursor = &pCur->base;
5467 return SQLITE_OK;
@@ -5564,15 +5569,16 @@
5564 ** is pointing at the first row, or pointing off the end of the table
5565 ** (so that seriesEof() will return true) if the table is empty.
5566 */
5567 static int seriesFilter(
5568 sqlite3_vtab_cursor *pVtabCursor,
5569 int idxNum, const char *idxStr,
5570 int argc, sqlite3_value **argv
5571 ){
5572 series_cursor *pCur = (series_cursor *)pVtabCursor;
5573 int i = 0;
 
5574 if( idxNum & 1 ){
5575 pCur->mnValue = sqlite3_value_int64(argv[i++]);
5576 }else{
5577 pCur->mnValue = 0;
5578 }
@@ -5625,11 +5631,11 @@
5625 ** (2) stop = $value -- constraint exists
5626 ** (4) step = $value -- constraint exists
5627 ** (8) output in descending order
5628 */
5629 static int seriesBestIndex(
5630 sqlite3_vtab *tab,
5631 sqlite3_index_info *pIdxInfo
5632 ){
5633 int i, j; /* Loop over constraints */
5634 int idxNum = 0; /* The query plan bitmask */
5635 int unusableMask = 0; /* Mask of unusable constraints */
@@ -5639,10 +5645,11 @@
5639
5640 /* This implementation assumes that the start, stop, and step columns
5641 ** are the last three columns in the virtual table. */
5642 assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
5643 assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
 
5644 aIdx[0] = aIdx[1] = aIdx[2] = -1;
5645 pConstraint = pIdxInfo->aConstraint;
5646 for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
5647 int iCol; /* 0 for start, 1 for stop, 2 for step */
5648 int iMask; /* bitmask for those column */
@@ -5712,10 +5719,14 @@
5712 0, /* xSync */
5713 0, /* xCommit */
5714 0, /* xRollback */
5715 0, /* xFindMethod */
5716 0, /* xRename */
 
 
 
 
5717 };
5718
5719 #endif /* SQLITE_OMIT_VIRTUALTABLE */
5720
5721 #ifdef _WIN32
@@ -14455,14 +14466,15 @@
14455 /*
14456 ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
14457 */
14458 static void shellUSleepFunc(
14459 sqlite3_context *context,
14460 int argc,
14461 sqlite3_value **argv
14462 ){
14463 int sleep = sqlite3_value_int(argv[0]);
 
14464 sqlite3_sleep(sleep/1000);
14465 sqlite3_result_int(context, sleep);
14466 }
14467
14468 /*
@@ -20657,12 +20669,15 @@
20657 p->in = fopen(sqliterc,"rb");
20658 if( p->in ){
20659 if( stdin_is_interactive ){
20660 utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc);
20661 }
20662 process_input(p);
20663 fclose(p->in);
 
 
 
20664 }
20665 p->in = inSaved;
20666 p->lineno = savedLineno;
20667 sqlite3_free(zBuf);
20668 }
@@ -21044,10 +21059,12 @@
21044 ** command, so ignore them */
21045 break;
21046 #endif
21047 }else if( strcmp(z, "-memtrace")==0 ){
21048 sqlite3MemTraceActivate(stderr);
 
 
21049 }
21050 }
21051 verify_uninitialized();
21052
21053
@@ -21190,11 +21207,11 @@
21190 ** prior to sending the SQL into SQLite. Useful for injecting
21191 ** crazy bytes in the middle of SQL statements for testing and debugging.
21192 */
21193 ShellSetFlag(&data, SHFLG_Backslash);
21194 }else if( strcmp(z,"-bail")==0 ){
21195 bail_on_error = 1;
21196 }else if( strcmp(z,"-version")==0 ){
21197 printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
21198 return 0;
21199 }else if( strcmp(z,"-interactive")==0 ){
21200 stdin_is_interactive = 1;
@@ -21278,24 +21295,29 @@
21278 ** the database filename.
21279 */
21280 for(i=0; i<nCmd; i++){
21281 if( azCmd[i][0]=='.' ){
21282 rc = do_meta_command(azCmd[i], &data);
21283 if( rc ) return rc==2 ? 0 : rc;
 
 
 
21284 }else{
21285 open_db(&data, 0);
21286 rc = shell_exec(&data, azCmd[i], &zErrMsg);
21287 if( zErrMsg!=0 ){
21288 utf8_printf(stderr,"Error: %s\n", zErrMsg);
 
 
 
 
 
 
21289 return rc!=0 ? rc : 1;
21290 }else if( rc!=0 ){
21291 utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
21292 return rc;
21293 }
21294 }
21295 }
21296 free(azCmd);
21297 }else{
21298 /* Run commands received from standard input
21299 */
21300 if( stdin_is_interactive ){
21301 char *zHome;
@@ -21337,10 +21359,11 @@
21337 }else{
21338 data.in = stdin;
21339 rc = process_input(&data);
21340 }
21341 }
 
21342 set_table_name(&data, 0);
21343 if( data.db ){
21344 session_close_all(&data);
21345 close_db(data.db);
21346 }
21347
--- src/shell.c
+++ src/shell.c
@@ -5420,14 +5420,14 @@
5420 ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
5421 ** result set of queries against generate_series will look like.
5422 */
5423 static int seriesConnect(
5424 sqlite3 *db,
5425 void *pUnused,
5426 int argcUnused, const char *const*argvUnused,
5427 sqlite3_vtab **ppVtab,
5428 char **pzErrUnused
5429 ){
5430 sqlite3_vtab *pNew;
5431 int rc;
5432
5433 /* Column numbers */
@@ -5434,10 +5434,14 @@
5434 #define SERIES_COLUMN_VALUE 0
5435 #define SERIES_COLUMN_START 1
5436 #define SERIES_COLUMN_STOP 2
5437 #define SERIES_COLUMN_STEP 3
5438
5439 (void)pUnused;
5440 (void)argcUnused;
5441 (void)argvUnused;
5442 (void)pzErrUnused;
5443 rc = sqlite3_declare_vtab(db,
5444 "CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
5445 if( rc==SQLITE_OK ){
5446 pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
5447 if( pNew==0 ) return SQLITE_NOMEM;
@@ -5456,12 +5460,13 @@
5460 }
5461
5462 /*
5463 ** Constructor for a new series_cursor object.
5464 */
5465 static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
5466 series_cursor *pCur;
5467 (void)pUnused;
5468 pCur = sqlite3_malloc( sizeof(*pCur) );
5469 if( pCur==0 ) return SQLITE_NOMEM;
5470 memset(pCur, 0, sizeof(*pCur));
5471 *ppCursor = &pCur->base;
5472 return SQLITE_OK;
@@ -5564,15 +5569,16 @@
5569 ** is pointing at the first row, or pointing off the end of the table
5570 ** (so that seriesEof() will return true) if the table is empty.
5571 */
5572 static int seriesFilter(
5573 sqlite3_vtab_cursor *pVtabCursor,
5574 int idxNum, const char *idxStrUnused,
5575 int argc, sqlite3_value **argv
5576 ){
5577 series_cursor *pCur = (series_cursor *)pVtabCursor;
5578 int i = 0;
5579 (void)idxStrUnused;
5580 if( idxNum & 1 ){
5581 pCur->mnValue = sqlite3_value_int64(argv[i++]);
5582 }else{
5583 pCur->mnValue = 0;
5584 }
@@ -5625,11 +5631,11 @@
5631 ** (2) stop = $value -- constraint exists
5632 ** (4) step = $value -- constraint exists
5633 ** (8) output in descending order
5634 */
5635 static int seriesBestIndex(
5636 sqlite3_vtab *tabUnused,
5637 sqlite3_index_info *pIdxInfo
5638 ){
5639 int i, j; /* Loop over constraints */
5640 int idxNum = 0; /* The query plan bitmask */
5641 int unusableMask = 0; /* Mask of unusable constraints */
@@ -5639,10 +5645,11 @@
5645
5646 /* This implementation assumes that the start, stop, and step columns
5647 ** are the last three columns in the virtual table. */
5648 assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
5649 assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
5650 (void)tabUnused;
5651 aIdx[0] = aIdx[1] = aIdx[2] = -1;
5652 pConstraint = pIdxInfo->aConstraint;
5653 for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
5654 int iCol; /* 0 for start, 1 for stop, 2 for step */
5655 int iMask; /* bitmask for those column */
@@ -5712,10 +5719,14 @@
5719 0, /* xSync */
5720 0, /* xCommit */
5721 0, /* xRollback */
5722 0, /* xFindMethod */
5723 0, /* xRename */
5724 0, /* xSavepoint */
5725 0, /* xRelease */
5726 0, /* xRollbackTo */
5727 0 /* xShadowName */
5728 };
5729
5730 #endif /* SQLITE_OMIT_VIRTUALTABLE */
5731
5732 #ifdef _WIN32
@@ -14455,14 +14466,15 @@
14466 /*
14467 ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
14468 */
14469 static void shellUSleepFunc(
14470 sqlite3_context *context,
14471 int argcUnused,
14472 sqlite3_value **argv
14473 ){
14474 int sleep = sqlite3_value_int(argv[0]);
14475 (void)argcUnused;
14476 sqlite3_sleep(sleep/1000);
14477 sqlite3_result_int(context, sleep);
14478 }
14479
14480 /*
@@ -20657,12 +20669,15 @@
20669 p->in = fopen(sqliterc,"rb");
20670 if( p->in ){
20671 if( stdin_is_interactive ){
20672 utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc);
20673 }
20674 if( process_input(p) && bail_on_error ) exit(1);
20675 fclose(p->in);
20676 }else if( sqliterc_override!=0 ){
20677 utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc);
20678 if( bail_on_error ) exit(1);
20679 }
20680 p->in = inSaved;
20681 p->lineno = savedLineno;
20682 sqlite3_free(zBuf);
20683 }
@@ -21044,10 +21059,12 @@
21059 ** command, so ignore them */
21060 break;
21061 #endif
21062 }else if( strcmp(z, "-memtrace")==0 ){
21063 sqlite3MemTraceActivate(stderr);
21064 }else if( strcmp(z,"-bail")==0 ){
21065 bail_on_error = 1;
21066 }
21067 }
21068 verify_uninitialized();
21069
21070
@@ -21190,11 +21207,11 @@
21207 ** prior to sending the SQL into SQLite. Useful for injecting
21208 ** crazy bytes in the middle of SQL statements for testing and debugging.
21209 */
21210 ShellSetFlag(&data, SHFLG_Backslash);
21211 }else if( strcmp(z,"-bail")==0 ){
21212 /* No-op. The bail_on_error flag should already be set. */
21213 }else if( strcmp(z,"-version")==0 ){
21214 printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
21215 return 0;
21216 }else if( strcmp(z,"-interactive")==0 ){
21217 stdin_is_interactive = 1;
@@ -21278,24 +21295,29 @@
21295 ** the database filename.
21296 */
21297 for(i=0; i<nCmd; i++){
21298 if( azCmd[i][0]=='.' ){
21299 rc = do_meta_command(azCmd[i], &data);
21300 if( rc ){
21301 free(azCmd);
21302 return rc==2 ? 0 : rc;
21303 }
21304 }else{
21305 open_db(&data, 0);
21306 rc = shell_exec(&data, azCmd[i], &zErrMsg);
21307 if( zErrMsg || rc ){
21308 if( zErrMsg!=0 ){
21309 utf8_printf(stderr,"Error: %s\n", zErrMsg);
21310 }else{
21311 utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
21312 }
21313 sqlite3_free(zErrMsg);
21314 free(azCmd);
21315 return rc!=0 ? rc : 1;
 
 
 
21316 }
21317 }
21318 }
 
21319 }else{
21320 /* Run commands received from standard input
21321 */
21322 if( stdin_is_interactive ){
21323 char *zHome;
@@ -21337,10 +21359,11 @@
21359 }else{
21360 data.in = stdin;
21361 rc = process_input(&data);
21362 }
21363 }
21364 free(azCmd);
21365 set_table_name(&data, 0);
21366 if( data.db ){
21367 session_close_all(&data);
21368 close_db(data.db);
21369 }
21370
+93 -13
--- src/sitemap.c
+++ src/sitemap.c
@@ -100,14 +100,11 @@
100100
}
101101
if( g.perm.Read ){
102102
@ <li>%z(href("%R/timeline"))Project Timeline</a>
103103
@ <ul>
104104
@ <li>%z(href("%R/reports"))Activity Reports</a></li>
105
- @ <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li>
106
- @ <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li>
107
- @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10
108
- @ check-ins</a></li>
105
+ @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li>
109106
@ </ul>
110107
@ </li>
111108
}
112109
if( g.perm.Read ){
113110
@ <li>%z(href("%R/brlist"))Branches</a>
@@ -192,11 +189,10 @@
192189
if( g.perm.Admin ){
193190
@ <li>%z(href("%R/urllist"))List of URLs used to access
194191
@ this repository</a></li>
195192
}
196193
@ <li>%z(href("%R/bloblist"))List of Artifacts</a></li>
197
- @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li>
198194
@ </ul>
199195
@ </li>
200196
}
201197
@ <li>%z(href("%R/help"))Help</a>
202198
@ <ul>
@@ -219,22 +215,106 @@
219215
@ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li>
220216
@ <li>%z(href("%R/admin_log"))Admin log</a></li>
221217
@ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li>
222218
@ </ul></li>
223219
}
224
- @ <li>Test Pages
225
- @ <ul>
220
+ @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li>
221
+ if( isPopup ){
222
+ @ <li>%z(href("%R/sitemap"))Site Map</a></li>
223
+ }
224
+ @ </ul>
225
+ if( !isPopup ){
226
+ style_finish_page("sitemap");
227
+ }
228
+}
229
+
230
+/*
231
+** WEBPAGE: sitemap-test
232
+**
233
+** List some of the web pages offered by the Fossil web engine for testing
234
+** purposes. This is similar to /sitemap, but is focused only on showing
235
+** pages associated with testing.
236
+*/
237
+void sitemap_test_page(void){
238
+ int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
239
+
240
+ login_check_credentials();
241
+ if( P("popup")!=0 && cgi_csrf_safe(0) ){
242
+ /* If this is a POST from the same origin with the popup=1 parameter,
243
+ ** then disable anti-robot defenses */
244
+ isPopup = 1;
245
+ g.perm.Hyperlink = 1;
246
+ g.javascriptHyperlink = 0;
247
+ }
248
+ if( !isPopup ){
249
+ style_header("Test Page Map");
250
+ style_adunit_config(ADUNIT_RIGHT_OK);
251
+ }
252
+ @ <ul id="sitemap" class="columns" style="column-width:20em">
226253
if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){
227
- @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li>
254
+ @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li>
228255
}
229256
if( g.perm.Read ){
230
- @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
257
+ @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
258
+ }
259
+ @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
260
+ @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
261
+ @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic
262
+ @ colors assigned to branch names</a>
263
+ if( g.perm.Admin ){
264
+ @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
265
+ @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
266
+ @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
267
+ @ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
268
+ @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a>
269
+ @ <li>%z(href("%R/repo_schema"))Repository schema</a></li>
270
+ }
271
+ if( g.perm.Read && g.perm.Hyperlink ){
272
+ @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li>
273
+ }
274
+ @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li>
275
+ @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
276
+ @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li>
277
+ if( !isPopup ){
278
+ style_finish_page("sitemap");
279
+ }
280
+}
281
+
282
+/*
283
+** WEBPAGE: sitemap-timeline
284
+**
285
+** Generate a list of hyperlinks to various (obscure) variations on
286
+** the /timeline page.
287
+*/
288
+void sitemap_timeline_page(void){
289
+ int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
290
+
291
+ login_check_credentials();
292
+ if( P("popup")!=0 && cgi_csrf_safe(0) ){
293
+ /* If this is a POST from the same origin with the popup=1 parameter,
294
+ ** then disable anti-robot defenses */
295
+ isPopup = 1;
296
+ g.perm.Hyperlink = 1;
297
+ g.javascriptHyperlink = 0;
298
+ }
299
+ if( !isPopup ){
300
+ style_header("Timeline Examples");
301
+ style_adunit_config(ADUNIT_RIGHT_OK);
231302
}
232
- @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic
233
- @ colors assigned to branch names</a>
234
- @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
235
- @ </ul></li>
303
+ @ <ul id="sitemap" class="columns" style="column-width:20em">
304
+ @ <li>%z(href("%R/timeline?ymd"))Current day</a></li>
305
+ @ <li>%z(href("%R/timeline?yw"))Current week</a></li>
306
+ @ <li>%z(href("%R/timeline?ym"))Current month</a></li>
307
+ @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li>
308
+ @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10
309
+ @ check-ins</a></li>
310
+ @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li>
311
+ @ <li>%z(href("%R/timeline?forks"))Forks</a></li>
312
+ @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li>
313
+ @ <li>%z(href("%R/timewarps"))Timewarps</a></li>
314
+ @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li>
315
+ @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li>
236316
@ </ul>
237317
if( !isPopup ){
238318
style_finish_page("sitemap");
239319
}
240320
}
241321
--- src/sitemap.c
+++ src/sitemap.c
@@ -100,14 +100,11 @@
100 }
101 if( g.perm.Read ){
102 @ <li>%z(href("%R/timeline"))Project Timeline</a>
103 @ <ul>
104 @ <li>%z(href("%R/reports"))Activity Reports</a></li>
105 @ <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li>
106 @ <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li>
107 @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10
108 @ check-ins</a></li>
109 @ </ul>
110 @ </li>
111 }
112 if( g.perm.Read ){
113 @ <li>%z(href("%R/brlist"))Branches</a>
@@ -192,11 +189,10 @@
192 if( g.perm.Admin ){
193 @ <li>%z(href("%R/urllist"))List of URLs used to access
194 @ this repository</a></li>
195 }
196 @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li>
197 @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li>
198 @ </ul>
199 @ </li>
200 }
201 @ <li>%z(href("%R/help"))Help</a>
202 @ <ul>
@@ -219,22 +215,106 @@
219 @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li>
220 @ <li>%z(href("%R/admin_log"))Admin log</a></li>
221 @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li>
222 @ </ul></li>
223 }
224 @ <li>Test Pages
225 @ <ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226 if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){
227 @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li>
228 }
229 if( g.perm.Read ){
230 @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231 }
232 @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic
233 @ colors assigned to branch names</a>
234 @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
235 @ </ul></li>
 
 
 
 
 
 
 
 
 
236 @ </ul>
237 if( !isPopup ){
238 style_finish_page("sitemap");
239 }
240 }
241
--- src/sitemap.c
+++ src/sitemap.c
@@ -100,14 +100,11 @@
100 }
101 if( g.perm.Read ){
102 @ <li>%z(href("%R/timeline"))Project Timeline</a>
103 @ <ul>
104 @ <li>%z(href("%R/reports"))Activity Reports</a></li>
105 @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li>
 
 
 
106 @ </ul>
107 @ </li>
108 }
109 if( g.perm.Read ){
110 @ <li>%z(href("%R/brlist"))Branches</a>
@@ -192,11 +189,10 @@
189 if( g.perm.Admin ){
190 @ <li>%z(href("%R/urllist"))List of URLs used to access
191 @ this repository</a></li>
192 }
193 @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li>
 
194 @ </ul>
195 @ </li>
196 }
197 @ <li>%z(href("%R/help"))Help</a>
198 @ <ul>
@@ -219,22 +215,106 @@
215 @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li>
216 @ <li>%z(href("%R/admin_log"))Admin log</a></li>
217 @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li>
218 @ </ul></li>
219 }
220 @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li>
221 if( isPopup ){
222 @ <li>%z(href("%R/sitemap"))Site Map</a></li>
223 }
224 @ </ul>
225 if( !isPopup ){
226 style_finish_page("sitemap");
227 }
228 }
229
230 /*
231 ** WEBPAGE: sitemap-test
232 **
233 ** List some of the web pages offered by the Fossil web engine for testing
234 ** purposes. This is similar to /sitemap, but is focused only on showing
235 ** pages associated with testing.
236 */
237 void sitemap_test_page(void){
238 int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
239
240 login_check_credentials();
241 if( P("popup")!=0 && cgi_csrf_safe(0) ){
242 /* If this is a POST from the same origin with the popup=1 parameter,
243 ** then disable anti-robot defenses */
244 isPopup = 1;
245 g.perm.Hyperlink = 1;
246 g.javascriptHyperlink = 0;
247 }
248 if( !isPopup ){
249 style_header("Test Page Map");
250 style_adunit_config(ADUNIT_RIGHT_OK);
251 }
252 @ <ul id="sitemap" class="columns" style="column-width:20em">
253 if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){
254 @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li>
255 }
256 if( g.perm.Read ){
257 @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
258 }
259 @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
260 @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
261 @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic
262 @ colors assigned to branch names</a>
263 if( g.perm.Admin ){
264 @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
265 @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
266 @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
267 @ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
268 @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a>
269 @ <li>%z(href("%R/repo_schema"))Repository schema</a></li>
270 }
271 if( g.perm.Read && g.perm.Hyperlink ){
272 @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li>
273 }
274 @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li>
275 @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
276 @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li>
277 if( !isPopup ){
278 style_finish_page("sitemap");
279 }
280 }
281
282 /*
283 ** WEBPAGE: sitemap-timeline
284 **
285 ** Generate a list of hyperlinks to various (obscure) variations on
286 ** the /timeline page.
287 */
288 void sitemap_timeline_page(void){
289 int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
290
291 login_check_credentials();
292 if( P("popup")!=0 && cgi_csrf_safe(0) ){
293 /* If this is a POST from the same origin with the popup=1 parameter,
294 ** then disable anti-robot defenses */
295 isPopup = 1;
296 g.perm.Hyperlink = 1;
297 g.javascriptHyperlink = 0;
298 }
299 if( !isPopup ){
300 style_header("Timeline Examples");
301 style_adunit_config(ADUNIT_RIGHT_OK);
302 }
303 @ <ul id="sitemap" class="columns" style="column-width:20em">
304 @ <li>%z(href("%R/timeline?ymd"))Current day</a></li>
305 @ <li>%z(href("%R/timeline?yw"))Current week</a></li>
306 @ <li>%z(href("%R/timeline?ym"))Current month</a></li>
307 @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li>
308 @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10
309 @ check-ins</a></li>
310 @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li>
311 @ <li>%z(href("%R/timeline?forks"))Forks</a></li>
312 @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li>
313 @ <li>%z(href("%R/timewarps"))Timewarps</a></li>
314 @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li>
315 @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li>
316 @ </ul>
317 if( !isPopup ){
318 style_finish_page("sitemap");
319 }
320 }
321
--- src/skins.c
+++ src/skins.c
@@ -85,11 +85,13 @@
8585
*/
8686
static struct SkinDetail {
8787
const char *zName; /* Name of the detail */
8888
const char *zValue; /* Value of the detail */
8989
} aSkinDetail[] = {
90
+ { "pikchr-fontscale", "" },
9091
{ "pikchr-foreground", "" },
92
+ { "pikchr-scale", "" },
9193
{ "timeline-arrowheads", "1" },
9294
{ "timeline-circle-nodes", "0" },
9395
{ "timeline-color-graph-lines", "0" },
9496
{ "white-foreground", "0" },
9597
};
9698
--- src/skins.c
+++ src/skins.c
@@ -85,11 +85,13 @@
85 */
86 static struct SkinDetail {
87 const char *zName; /* Name of the detail */
88 const char *zValue; /* Value of the detail */
89 } aSkinDetail[] = {
 
90 { "pikchr-foreground", "" },
 
91 { "timeline-arrowheads", "1" },
92 { "timeline-circle-nodes", "0" },
93 { "timeline-color-graph-lines", "0" },
94 { "white-foreground", "0" },
95 };
96
--- src/skins.c
+++ src/skins.c
@@ -85,11 +85,13 @@
85 */
86 static struct SkinDetail {
87 const char *zName; /* Name of the detail */
88 const char *zValue; /* Value of the detail */
89 } aSkinDetail[] = {
90 { "pikchr-fontscale", "" },
91 { "pikchr-foreground", "" },
92 { "pikchr-scale", "" },
93 { "timeline-arrowheads", "1" },
94 { "timeline-circle-nodes", "0" },
95 { "timeline-color-graph-lines", "0" },
96 { "white-foreground", "0" },
97 };
98
+37 -13
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -32,10 +32,15 @@
3232
3333
#ifndef _WIN32
3434
# include "linenoise.h"
3535
#endif
3636
37
+/*
38
+** True if the "fossil sql" command has the --test flag. False otherwise.
39
+*/
40
+static int local_bSqlCmdTest = 0;
41
+
3742
/*
3843
** Implementation of the "content(X)" SQL function. Return the complete
3944
** content of artifact identified by X as a blob.
4045
*/
4146
static void sqlcmd_content(
@@ -163,33 +168,46 @@
163168
** Undocumented test SQL functions:
164169
**
165170
** db_protect(X)
166171
** db_protect_pop(X)
167172
**
168
-** These invoke the corresponding C routines. Misuse may result in
169
-** an assertion fault.
173
+** These invoke the corresponding C routines.
174
+**
175
+** WARNING:
176
+** Do not instantiate these functions for any Fossil webpage or command
177
+** method of than the "fossil sql" command. If an attacker gains access
178
+** to these functions, he will be able to disable other defense mechanisms.
179
+**
180
+** This routines are for interactiving testing only. They are experimental
181
+** and undocumented (apart from this comments) and might go away or change
182
+** in future releases.
183
+**
184
+** 2020-11-29: This functions are now only available if the "fossil sql"
185
+** command is started with the --test option.
170186
*/
171187
static void sqlcmd_db_protect(
172188
sqlite3_context *context,
173189
int argc,
174190
sqlite3_value **argv
175191
){
176192
unsigned mask = 0;
177193
const char *z = (const char*)sqlite3_value_text(argv[0]);
178
- if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER;
179
- if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG;
180
- if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE;
181
- if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY;
182
- if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL;
183
- db_protect(mask);
194
+ if( z!=0 && local_bSqlCmdTest ){
195
+ if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER;
196
+ if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG;
197
+ if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE;
198
+ if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY;
199
+ if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL;
200
+ db_protect(mask);
201
+ }
184202
}
185203
static void sqlcmd_db_protect_pop(
186204
sqlite3_context *context,
187205
int argc,
188206
sqlite3_value **argv
189207
){
190
- db_protect_pop();
208
+ if( !local_bSqlCmdTest ) db_protect_pop();
191209
}
192210
193211
194212
195213
@@ -232,14 +250,16 @@
232250
** will get cleaned up when the shell closes the database connection */
233251
if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
234252
sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
235253
db_protect_only(PROTECT_NONE);
236254
sqlite3_set_authorizer(db, db_top_authorizer, db);
237
- sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0,
238
- sqlcmd_db_protect, 0, 0);
239
- sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0,
240
- sqlcmd_db_protect_pop, 0, 0);
255
+ if( local_bSqlCmdTest ){
256
+ sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0,
257
+ sqlcmd_db_protect, 0, 0);
258
+ sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0,
259
+ sqlcmd_db_protect_pop, 0, 0);
260
+ }
241261
return SQLITE_OK;
242262
}
243263
244264
/*
245265
** atexit() handler that cleans up global state modified by this module.
@@ -329,10 +349,13 @@
329349
** --readonly Open the repository read-only. No changes
330350
** are allowed. This is a recommended safety
331351
** precaution to prevent repository damage.
332352
**
333353
** -R REPOSITORY Use REPOSITORY as the repository database
354
+**
355
+** --test Enable some testing and analysis features
356
+** that are normally disabled.
334357
**
335358
** All of the standard sqlite3 command-line shell options should also
336359
** work.
337360
**
338361
** The following SQL extensions are provided with this Fossil-enhanced
@@ -397,10 +420,11 @@
397420
extern int sqlite3_shell(int, char**);
398421
#ifdef FOSSIL_ENABLE_TH1_HOOKS
399422
g.fNoThHook = 1;
400423
#endif
401424
noRepository = find_option("no-repository", 0, 0)!=0;
425
+ local_bSqlCmdTest = find_option("test",0,0)!=0;
402426
if( !noRepository ){
403427
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
404428
}
405429
db_open_config(1,0);
406430
zConfigDb = fossil_strdup(g.zConfigDbName);
407431
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -32,10 +32,15 @@
32
33 #ifndef _WIN32
34 # include "linenoise.h"
35 #endif
36
 
 
 
 
 
37 /*
38 ** Implementation of the "content(X)" SQL function. Return the complete
39 ** content of artifact identified by X as a blob.
40 */
41 static void sqlcmd_content(
@@ -163,33 +168,46 @@
163 ** Undocumented test SQL functions:
164 **
165 ** db_protect(X)
166 ** db_protect_pop(X)
167 **
168 ** These invoke the corresponding C routines. Misuse may result in
169 ** an assertion fault.
 
 
 
 
 
 
 
 
 
 
 
170 */
171 static void sqlcmd_db_protect(
172 sqlite3_context *context,
173 int argc,
174 sqlite3_value **argv
175 ){
176 unsigned mask = 0;
177 const char *z = (const char*)sqlite3_value_text(argv[0]);
178 if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER;
179 if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG;
180 if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE;
181 if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY;
182 if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL;
183 db_protect(mask);
 
 
184 }
185 static void sqlcmd_db_protect_pop(
186 sqlite3_context *context,
187 int argc,
188 sqlite3_value **argv
189 ){
190 db_protect_pop();
191 }
192
193
194
195
@@ -232,14 +250,16 @@
232 ** will get cleaned up when the shell closes the database connection */
233 if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
234 sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
235 db_protect_only(PROTECT_NONE);
236 sqlite3_set_authorizer(db, db_top_authorizer, db);
237 sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0,
238 sqlcmd_db_protect, 0, 0);
239 sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0,
240 sqlcmd_db_protect_pop, 0, 0);
 
 
241 return SQLITE_OK;
242 }
243
244 /*
245 ** atexit() handler that cleans up global state modified by this module.
@@ -329,10 +349,13 @@
329 ** --readonly Open the repository read-only. No changes
330 ** are allowed. This is a recommended safety
331 ** precaution to prevent repository damage.
332 **
333 ** -R REPOSITORY Use REPOSITORY as the repository database
 
 
 
334 **
335 ** All of the standard sqlite3 command-line shell options should also
336 ** work.
337 **
338 ** The following SQL extensions are provided with this Fossil-enhanced
@@ -397,10 +420,11 @@
397 extern int sqlite3_shell(int, char**);
398 #ifdef FOSSIL_ENABLE_TH1_HOOKS
399 g.fNoThHook = 1;
400 #endif
401 noRepository = find_option("no-repository", 0, 0)!=0;
 
402 if( !noRepository ){
403 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
404 }
405 db_open_config(1,0);
406 zConfigDb = fossil_strdup(g.zConfigDbName);
407
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -32,10 +32,15 @@
32
33 #ifndef _WIN32
34 # include "linenoise.h"
35 #endif
36
37 /*
38 ** True if the "fossil sql" command has the --test flag. False otherwise.
39 */
40 static int local_bSqlCmdTest = 0;
41
42 /*
43 ** Implementation of the "content(X)" SQL function. Return the complete
44 ** content of artifact identified by X as a blob.
45 */
46 static void sqlcmd_content(
@@ -163,33 +168,46 @@
168 ** Undocumented test SQL functions:
169 **
170 ** db_protect(X)
171 ** db_protect_pop(X)
172 **
173 ** These invoke the corresponding C routines.
174 **
175 ** WARNING:
176 ** Do not instantiate these functions for any Fossil webpage or command
177 ** method of than the "fossil sql" command. If an attacker gains access
178 ** to these functions, he will be able to disable other defense mechanisms.
179 **
180 ** This routines are for interactiving testing only. They are experimental
181 ** and undocumented (apart from this comments) and might go away or change
182 ** in future releases.
183 **
184 ** 2020-11-29: This functions are now only available if the "fossil sql"
185 ** command is started with the --test option.
186 */
187 static void sqlcmd_db_protect(
188 sqlite3_context *context,
189 int argc,
190 sqlite3_value **argv
191 ){
192 unsigned mask = 0;
193 const char *z = (const char*)sqlite3_value_text(argv[0]);
194 if( z!=0 && local_bSqlCmdTest ){
195 if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER;
196 if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG;
197 if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE;
198 if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY;
199 if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL;
200 db_protect(mask);
201 }
202 }
203 static void sqlcmd_db_protect_pop(
204 sqlite3_context *context,
205 int argc,
206 sqlite3_value **argv
207 ){
208 if( !local_bSqlCmdTest ) db_protect_pop();
209 }
210
211
212
213
@@ -232,14 +250,16 @@
250 ** will get cleaned up when the shell closes the database connection */
251 if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
252 sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
253 db_protect_only(PROTECT_NONE);
254 sqlite3_set_authorizer(db, db_top_authorizer, db);
255 if( local_bSqlCmdTest ){
256 sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0,
257 sqlcmd_db_protect, 0, 0);
258 sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0,
259 sqlcmd_db_protect_pop, 0, 0);
260 }
261 return SQLITE_OK;
262 }
263
264 /*
265 ** atexit() handler that cleans up global state modified by this module.
@@ -329,10 +349,13 @@
349 ** --readonly Open the repository read-only. No changes
350 ** are allowed. This is a recommended safety
351 ** precaution to prevent repository damage.
352 **
353 ** -R REPOSITORY Use REPOSITORY as the repository database
354 **
355 ** --test Enable some testing and analysis features
356 ** that are normally disabled.
357 **
358 ** All of the standard sqlite3 command-line shell options should also
359 ** work.
360 **
361 ** The following SQL extensions are provided with this Fossil-enhanced
@@ -397,10 +420,11 @@
420 extern int sqlite3_shell(int, char**);
421 #ifdef FOSSIL_ENABLE_TH1_HOOKS
422 g.fNoThHook = 1;
423 #endif
424 noRepository = find_option("no-repository", 0, 0)!=0;
425 local_bSqlCmdTest = find_option("test",0,0)!=0;
426 if( !noRepository ){
427 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
428 }
429 db_open_config(1,0);
430 zConfigDb = fossil_strdup(g.zConfigDbName);
431
+215 -74
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1171,11 +1171,11 @@
11711171
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
11721172
** [sqlite_version()] and [sqlite_source_id()].
11731173
*/
11741174
#define SQLITE_VERSION "3.34.0"
11751175
#define SQLITE_VERSION_NUMBER 3034000
1176
-#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d"
1176
+#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
11771177
11781178
/*
11791179
** CAPI3REF: Run-Time Library Version Numbers
11801180
** KEYWORDS: sqlite3_version sqlite3_sourceid
11811181
**
@@ -1550,10 +1550,11 @@
15501550
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
15511551
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
15521552
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
15531553
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
15541554
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
1555
+#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
15551556
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
15561557
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
15571558
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
15581559
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
15591560
#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -7238,11 +7239,11 @@
72387239
** CAPI3REF: Determine the transaction state of a database
72397240
** METHOD: sqlite3
72407241
**
72417242
** ^The sqlite3_txn_state(D,S) interface returns the current
72427243
** [transaction state] of schema S in database connection D. ^If S is NULL,
7243
-** then the highest transaction state of any schema on databse connection D
7244
+** then the highest transaction state of any schema on database connection D
72447245
** is returned. Transaction states are (in order of lowest to highest):
72457246
** <ol>
72467247
** <li value="0"> SQLITE_TXN_NONE
72477248
** <li value="1"> SQLITE_TXN_READ
72487249
** <li value="2"> SQLITE_TXN_WRITE
@@ -8785,11 +8786,10 @@
87858786
*/
87868787
#define SQLITE_TESTCTRL_FIRST 5
87878788
#define SQLITE_TESTCTRL_PRNG_SAVE 5
87888789
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
87898790
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
8790
-#define SQLITE_TESTCTRL_SEEK_COUNT 7
87918791
#define SQLITE_TESTCTRL_BITVEC_TEST 8
87928792
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
87938793
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
87948794
#define SQLITE_TESTCTRL_PENDING_BYTE 11
87958795
#define SQLITE_TESTCTRL_ASSERT 12
@@ -8810,11 +8810,12 @@
88108810
#define SQLITE_TESTCTRL_IMPOSTER 25
88118811
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
88128812
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
88138813
#define SQLITE_TESTCTRL_PRNG_SEED 28
88148814
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
8815
-#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
8815
+#define SQLITE_TESTCTRL_SEEK_COUNT 30
8816
+#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */
88168817
88178818
/*
88188819
** CAPI3REF: SQL Keyword Checking
88198820
**
88208821
** These routines provide access to the set of SQL language keywords
@@ -28182,16 +28183,19 @@
2818228183
EnableLookaside;
2818328184
}
2818428185
}
2818528186
2818628187
/*
28187
-** Take actions at the end of an API call to indicate an OOM error
28188
+** Take actions at the end of an API call to deal with error codes.
2818828189
*/
28189
-static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
28190
- sqlite3OomClear(db);
28191
- sqlite3Error(db, SQLITE_NOMEM);
28192
- return SQLITE_NOMEM_BKPT;
28190
+static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){
28191
+ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
28192
+ sqlite3OomClear(db);
28193
+ sqlite3Error(db, SQLITE_NOMEM);
28194
+ return SQLITE_NOMEM_BKPT;
28195
+ }
28196
+ return rc & db->errMask;
2819328197
}
2819428198
2819528199
/*
2819628200
** This function must be called before exiting any API function (i.e.
2819728201
** returning control to the user) that has called sqlite3_malloc or
@@ -28209,12 +28213,12 @@
2820928213
** Otherwise the read (and possible write) of db->mallocFailed
2821028214
** is unsafe, as is the call to sqlite3Error().
2821128215
*/
2821228216
assert( db!=0 );
2821328217
assert( sqlite3_mutex_held(db->mutex) );
28214
- if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
28215
- return apiOomError(db);
28218
+ if( db->mallocFailed || rc ){
28219
+ return apiHandleError(db, rc);
2821628220
}
2821728221
return rc & db->errMask;
2821828222
}
2821928223
2822028224
/************** End of malloc.c **********************************************/
@@ -37003,11 +37007,28 @@
3700337007
3700437008
got = seekAndRead(pFile, offset, pBuf, amt);
3700537009
if( got==amt ){
3700637010
return SQLITE_OK;
3700737011
}else if( got<0 ){
37008
- /* lastErrno set by seekAndRead */
37012
+ /* pFile->lastErrno has been set by seekAndRead().
37013
+ ** Usually we return SQLITE_IOERR_READ here, though for some
37014
+ ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The
37015
+ ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT
37016
+ ** prior to returning to the application by the sqlite3ApiExit()
37017
+ ** routine.
37018
+ */
37019
+ switch( pFile->lastErrno ){
37020
+ case ERANGE:
37021
+ case EIO:
37022
+#ifdef ENXIO
37023
+ case ENXIO:
37024
+#endif
37025
+#ifdef EDEVERR
37026
+ case EDEVERR:
37027
+#endif
37028
+ return SQLITE_IOERR_CORRUPTFS;
37029
+ }
3700937030
return SQLITE_IOERR_READ;
3701037031
}else{
3701137032
storeLastErrno(pFile, 0); /* not a system error */
3701237033
/* Unread parts of the buffer must be zero-filled */
3701337034
memset(&((char*)pBuf)[got], 0, amt-got);
@@ -38535,11 +38556,11 @@
3853538556
if( bUnlock ){
3853638557
rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
3853738558
if( rc==SQLITE_OK ){
3853838559
memset(&aLock[ofst], 0, sizeof(int)*n);
3853938560
}
38540
- }else if( p->sharedMask & (1<<ofst) ){
38561
+ }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
3854138562
assert( n==1 && aLock[ofst]>1 );
3854238563
aLock[ofst]--;
3854338564
}
3854438565
3854538566
/* Undo the local locks */
@@ -38568,11 +38589,11 @@
3856838589
/* Make sure no sibling connections hold locks that will block this
3856938590
** lock. If any do, return SQLITE_BUSY right away. */
3857038591
int ii;
3857138592
for(ii=ofst; ii<ofst+n; ii++){
3857238593
assert( (p->sharedMask & mask)==0 );
38573
- if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){
38594
+ if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
3857438595
rc = SQLITE_BUSY;
3857538596
break;
3857638597
}
3857738598
}
3857838599
@@ -39964,19 +39985,39 @@
3996439985
}
3996539986
return SQLITE_OK;
3996639987
}
3996739988
3996839989
/*
39990
+** If the last component of the pathname in z[0]..z[j-1] is something
39991
+** other than ".." then back it out and return true. If the last
39992
+** component is empty or if it is ".." then return false.
39993
+*/
39994
+static int unixBackupDir(const char *z, int *pJ){
39995
+ int j = *pJ;
39996
+ int i;
39997
+ if( j<=0 ) return 0;
39998
+ for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){}
39999
+ if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
40000
+ *pJ = i-1;
40001
+ return 1;
40002
+}
40003
+
40004
+/*
40005
+** Convert a relative pathname into a full pathname. Also
40006
+** simplify the pathname as follows:
3996940007
**
40008
+** Remove all instances of /./
40009
+** Remove all isntances of /X/../ for any X
3997040010
*/
3997140011
static int mkFullPathname(
3997240012
const char *zPath, /* Input path */
3997340013
char *zOut, /* Output buffer */
3997440014
int nOut /* Allocated size of buffer zOut */
3997540015
){
3997640016
int nPath = sqlite3Strlen30(zPath);
3997740017
int iOff = 0;
40018
+ int i, j;
3997840019
if( zPath[0]!='/' ){
3997940020
if( osGetcwd(zOut, nOut-2)==0 ){
3998040021
return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
3998140022
}
3998240023
iOff = sqlite3Strlen30(zOut);
@@ -39987,10 +40028,45 @@
3998740028
** even if it returns an error. */
3998840029
zOut[iOff] = '\0';
3998940030
return SQLITE_CANTOPEN_BKPT;
3999040031
}
3999140032
sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
40033
+
40034
+ /* Remove duplicate '/' characters. Except, two // at the beginning
40035
+ ** of a pathname is allowed since this is important on windows. */
40036
+ for(i=j=1; zOut[i]; i++){
40037
+ zOut[j++] = zOut[i];
40038
+ while( zOut[i]=='/' && zOut[i+1]=='/' ) i++;
40039
+ }
40040
+ zOut[j] = 0;
40041
+
40042
+ assert( zOut[0]=='/' );
40043
+ for(i=j=0; zOut[i]; i++){
40044
+ if( zOut[i]=='/' ){
40045
+ /* Skip over internal "/." directory components */
40046
+ if( zOut[i+1]=='.' && zOut[i+2]=='/' ){
40047
+ i += 1;
40048
+ continue;
40049
+ }
40050
+
40051
+ /* If this is a "/.." directory component then back out the
40052
+ ** previous term of the directory if it is something other than "..".
40053
+ */
40054
+ if( zOut[i+1]=='.'
40055
+ && zOut[i+2]=='.'
40056
+ && zOut[i+3]=='/'
40057
+ && unixBackupDir(zOut, &j)
40058
+ ){
40059
+ i += 2;
40060
+ continue;
40061
+ }
40062
+ }
40063
+ if( ALWAYS(j>=0) ) zOut[j] = zOut[i];
40064
+ j++;
40065
+ }
40066
+ if( NEVER(j==0) ) zOut[j++] = '/';
40067
+ zOut[j] = 0;
3999240068
return SQLITE_OK;
3999340069
}
3999440070
3999540071
/*
3999640072
** Turn a relative pathname into a full pathname. The relative path
@@ -54326,10 +54402,11 @@
5432654402
sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */
5432754403
char *zSuperJournal = 0; /* Contents of super-journal file */
5432854404
i64 nSuperJournal; /* Size of super-journal file */
5432954405
char *zJournal; /* Pointer to one journal within MJ file */
5433054406
char *zSuperPtr; /* Space to hold super-journal filename */
54407
+ char *zFree = 0; /* Free this buffer */
5433154408
int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
5433254409
5433354410
/* Allocate space for both the pJournal and pSuper file descriptors.
5433454411
** If successful, open the super-journal file for reading.
5433554412
*/
@@ -54350,15 +54427,17 @@
5435054427
** files extracted from regular rollback-journals.
5435154428
*/
5435254429
rc = sqlite3OsFileSize(pSuper, &nSuperJournal);
5435354430
if( rc!=SQLITE_OK ) goto delsuper_out;
5435454431
nSuperPtr = pVfs->mxPathname+1;
54355
- zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2);
54356
- if( !zSuperJournal ){
54432
+ zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2);
54433
+ if( !zFree ){
5435754434
rc = SQLITE_NOMEM_BKPT;
5435854435
goto delsuper_out;
5435954436
}
54437
+ zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0;
54438
+ zSuperJournal = &zFree[4];
5436054439
zSuperPtr = &zSuperJournal[nSuperJournal+2];
5436154440
rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0);
5436254441
if( rc!=SQLITE_OK ) goto delsuper_out;
5436354442
zSuperJournal[nSuperJournal] = 0;
5436454443
zSuperJournal[nSuperJournal+1] = 0;
@@ -54402,11 +54481,11 @@
5440254481
5440354482
sqlite3OsClose(pSuper);
5440454483
rc = sqlite3OsDelete(pVfs, zSuper, 0);
5440554484
5440654485
delsuper_out:
54407
- sqlite3_free(zSuperJournal);
54486
+ sqlite3_free(zFree);
5440854487
if( pSuper ){
5440954488
sqlite3OsClose(pSuper);
5441054489
assert( !isOpen(pJournal) );
5441154490
sqlite3_free(pSuper);
5441254491
}
@@ -54740,11 +54819,15 @@
5474054819
** in case this has happened, clear the changeCountDone flag now.
5474154820
*/
5474254821
pPager->changeCountDone = pPager->tempFile;
5474354822
5474454823
if( rc==SQLITE_OK ){
54745
- zSuper = pPager->pTmpSpace;
54824
+ /* Leave 4 bytes of space before the super-journal filename in memory.
54825
+ ** This is because it may end up being passed to sqlite3OsOpen(), in
54826
+ ** which case it requires 4 0x00 bytes in memory immediately before
54827
+ ** the filename. */
54828
+ zSuper = &pPager->pTmpSpace[4];
5474654829
rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
5474754830
testcase( rc!=SQLITE_OK );
5474854831
}
5474954832
if( rc==SQLITE_OK
5475054833
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
@@ -54757,10 +54840,12 @@
5475754840
}
5475854841
if( rc==SQLITE_OK && zSuper[0] && res ){
5475954842
/* If there was a super-journal and this routine will return success,
5476054843
** see if it is possible to delete the super-journal.
5476154844
*/
54845
+ assert( zSuper==&pPager->pTmpSpace[4] );
54846
+ memset(&zSuper[-4], 0, 4);
5476254847
rc = pager_delsuper(pPager, zSuper);
5476354848
testcase( rc!=SQLITE_OK );
5476454849
}
5476554850
if( isHot && nPlayback ){
5476654851
sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s",
@@ -64746,11 +64831,11 @@
6474664831
#define hasReadConflicts(a, b) 0
6474764832
#endif
6474864833
6474964834
#ifdef SQLITE_DEBUG
6475064835
/*
64751
-** Return an reset the seek counter for a Btree object.
64836
+** Return and reset the seek counter for a Btree object.
6475264837
*/
6475364838
SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){
6475464839
u64 n = pBt->nSeek;
6475564840
pBt->nSeek = 0;
6475664841
return n;
@@ -82192,13 +82277,16 @@
8219282277
** equal to, or greater than the second (double).
8219382278
*/
8219482279
static int sqlite3IntFloatCompare(i64 i, double r){
8219582280
if( sizeof(LONGDOUBLE_TYPE)>8 ){
8219682281
LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
82282
+ testcase( x<r );
82283
+ testcase( x>r );
82284
+ testcase( x==r );
8219782285
if( x<r ) return -1;
82198
- if( x>r ) return +1;
82199
- return 0;
82286
+ if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */
82287
+ return 0; /*NO_TEST*/ /* work around bugs in gcov */
8220082288
}else{
8220182289
i64 y;
8220282290
double s;
8220382291
if( r<-9223372036854775808.0 ) return +1;
8220482292
if( r>=9223372036854775808.0 ) return -1;
@@ -89388,11 +89476,11 @@
8938889476
assert( rc==SQLITE_OK );
8938989477
break;
8939089478
}
8939189479
8939289480
89393
-/* Opcode: OpenEphemeral P1 P2 * P4 P5
89481
+/* Opcode: OpenEphemeral P1 P2 P3 P4 P5
8939489482
** Synopsis: nColumn=P2
8939589483
**
8939689484
** Open a new cursor P1 to a transient table.
8939789485
** The cursor is always opened read/write even if
8939889486
** the main database is read-only. The ephemeral
@@ -89408,10 +89496,14 @@
8940889496
**
8940989497
** The P5 parameter can be a mask of the BTREE_* flags defined
8941089498
** in btree.h. These flags control aspects of the operation of
8941189499
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
8941289500
** added automatically.
89501
+**
89502
+** If P3 is positive, then reg[P3] is modified slightly so that it
89503
+** can be used as zero-length data for OP_Insert. This is an optimization
89504
+** that avoids an extra OP_Blob opcode to initialize that register.
8941389505
*/
8941489506
/* Opcode: OpenAutoindex P1 P2 * P4 *
8941589507
** Synopsis: nColumn=P2
8941689508
**
8941789509
** This opcode works the same as OP_OpenEphemeral. It has a
@@ -89430,10 +89522,19 @@
8943089522
SQLITE_OPEN_EXCLUSIVE |
8943189523
SQLITE_OPEN_DELETEONCLOSE |
8943289524
SQLITE_OPEN_TRANSIENT_DB;
8943389525
assert( pOp->p1>=0 );
8943489526
assert( pOp->p2>=0 );
89527
+ if( pOp->p3>0 ){
89528
+ /* Make register reg[P3] into a value that can be used as the data
89529
+ ** form sqlite3BtreeInsert() where the length of the data is zero. */
89530
+ assert( pOp->p2==0 ); /* Only used when number of columns is zero */
89531
+ assert( pOp->opcode==OP_OpenEphemeral );
89532
+ assert( aMem[pOp->p3].flags & MEM_Null );
89533
+ aMem[pOp->p3].n = 0;
89534
+ aMem[pOp->p3].z = "";
89535
+ }
8943589536
pCx = p->apCsr[pOp->p1];
8943689537
if( pCx && pCx->pBtx ){
8943789538
/* If the ephermeral table is already open, erase all existing content
8943889539
** so that the table is empty again, rather than creating a new table. */
8943989540
assert( pCx->isEphemeral );
@@ -90589,11 +90690,11 @@
9058990690
if( pOp->p5 & OPFLAG_ISNOOP ) break;
9059090691
#endif
9059190692
9059290693
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
9059390694
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
90594
- assert( pData->flags & (MEM_Blob|MEM_Str) );
90695
+ assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 );
9059590696
x.pData = pData->z;
9059690697
x.nData = pData->n;
9059790698
seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
9059890699
if( pData->flags & MEM_Zero ){
9059990700
x.nZero = pData->u.nZero;
@@ -93644,11 +93745,15 @@
9364493745
9364593746
/* If we reach this point, it means that execution is finished with
9364693747
** an error of some kind.
9364793748
*/
9364893749
abort_due_to_error:
93649
- if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT;
93750
+ if( db->mallocFailed ){
93751
+ rc = SQLITE_NOMEM_BKPT;
93752
+ }else if( rc==SQLITE_IOERR_CORRUPTFS ){
93753
+ rc = SQLITE_CORRUPT_BKPT;
93754
+ }
9365093755
assert( rc );
9365193756
if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
9365293757
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
9365393758
}
9365493759
p->rc = rc;
@@ -99381,10 +99486,11 @@
9938199486
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
9938299487
int iCol = -1;
9938399488
Expr *pE, *pDup;
9938499489
if( pItem->done ) continue;
9938599490
pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
99491
+ if( NEVER(pE==0) ) continue;
9938699492
if( sqlite3ExprIsInteger(pE, &iCol) ){
9938799493
if( iCol<=0 || iCol>pEList->nExpr ){
9938899494
resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
9938999495
return 1;
9939099496
}
@@ -99560,10 +99666,11 @@
9956099666
nResult = pSelect->pEList->nExpr;
9956199667
pParse = pNC->pParse;
9956299668
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
9956399669
Expr *pE = pItem->pExpr;
9956499670
Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
99671
+ if( NEVER(pE2==0) ) continue;
9956599672
if( zType[0]!='G' ){
9956699673
iCol = resolveAsName(pParse, pSelect->pEList, pE2);
9956799674
if( iCol>0 ){
9956899675
/* If an AS-name match is found, mark this ORDER BY column as being
9956999676
** a copy of the iCol-th result-set column. The subsequent call to
@@ -103679,10 +103786,11 @@
103679103786
** register iReg. The caller must ensure that iReg already contains
103680103787
** the correct value for the expression.
103681103788
*/
103682103789
static void exprToRegister(Expr *pExpr, int iReg){
103683103790
Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
103791
+ if( NEVER(p==0) ) return;
103684103792
p->op2 = p->op;
103685103793
p->op = TK_REGISTER;
103686103794
p->iTable = iReg;
103687103795
ExprClearProperty(p, EP_Skip);
103688103796
}
@@ -104666,10 +104774,11 @@
104666104774
*/
104667104775
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
104668104776
int r2;
104669104777
pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
104670104778
if( ConstFactorOk(pParse)
104779
+ && ALWAYS(pExpr!=0)
104671104780
&& pExpr->op!=TK_REGISTER
104672104781
&& sqlite3ExprIsConstantNotJoin(pExpr)
104673104782
){
104674104783
*pReg = 0;
104675104784
r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -119424,10 +119533,12 @@
119424119533
VFUNCTION(total_changes, 0, 0, 0, total_changes ),
119425119534
FUNCTION(replace, 3, 0, 0, replaceFunc ),
119426119535
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
119427119536
FUNCTION(substr, 2, 0, 0, substrFunc ),
119428119537
FUNCTION(substr, 3, 0, 0, substrFunc ),
119538
+ FUNCTION(substring, 2, 0, 0, substrFunc ),
119539
+ FUNCTION(substring, 3, 0, 0, substrFunc ),
119429119540
WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
119430119541
WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
119431119542
WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
119432119543
WAGGREGATE(count, 0,0,0, countStep,
119433119544
countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
@@ -124310,10 +124421,12 @@
124310124421
/* Version 3.32.0 and later */
124311124422
char *(*create_filename)(const char*,const char*,const char*,
124312124423
int,const char**);
124313124424
void (*free_filename)(char*);
124314124425
sqlite3_file *(*database_file_object)(const char*);
124426
+ /* Version 3.34.0 and later */
124427
+ int (*txn_state)(sqlite3*,const char*);
124315124428
};
124316124429
124317124430
/*
124318124431
** This is the function signature used for all extension entry points. It
124319124432
** is also defined in the file "loadext.c".
@@ -124614,10 +124727,12 @@
124614124727
#define sqlite3_filename_wal sqlite3_api->filename_wal
124615124728
/* Version 3.32.0 and later */
124616124729
#define sqlite3_create_filename sqlite3_api->create_filename
124617124730
#define sqlite3_free_filename sqlite3_api->free_filename
124618124731
#define sqlite3_database_file_object sqlite3_api->database_file_object
124732
+/* Version 3.34.0 and later */
124733
+#define sqlite3_txn_state sqlite3_api->txn_state
124619124734
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
124620124735
124621124736
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
124622124737
/* This case when the file really is being compiled as a loadable
124623124738
** extension */
@@ -125096,10 +125211,12 @@
125096125211
sqlite3_filename_wal,
125097125212
/* Version 3.32.0 and later */
125098125213
sqlite3_create_filename,
125099125214
sqlite3_free_filename,
125100125215
sqlite3_database_file_object,
125216
+ /* Version 3.34.0 and later */
125217
+ sqlite3_txn_state,
125101125218
};
125102125219
125103125220
/* True if x is the directory separator character
125104125221
*/
125105125222
#if SQLITE_OS_WIN
@@ -131654,10 +131771,11 @@
131654131771
Column *aCol, *pCol; /* For looping over result columns */
131655131772
int nCol; /* Number of columns in the result set */
131656131773
char *zName; /* Column name */
131657131774
int nName; /* Size of name in zName[] */
131658131775
Hash ht; /* Hash table of column names */
131776
+ Table *pTab;
131659131777
131660131778
sqlite3HashInit(&ht);
131661131779
if( pEList ){
131662131780
nCol = pEList->nExpr;
131663131781
aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
@@ -131676,19 +131794,17 @@
131676131794
*/
131677131795
if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){
131678131796
/* If the column contains an "AS <name>" phrase, use <name> as the name */
131679131797
}else{
131680131798
Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
131681
- while( pColExpr->op==TK_DOT ){
131799
+ while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){
131682131800
pColExpr = pColExpr->pRight;
131683131801
assert( pColExpr!=0 );
131684131802
}
131685
- if( pColExpr->op==TK_COLUMN ){
131803
+ if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){
131686131804
/* For columns use the column name name */
131687131805
int iCol = pColExpr->iColumn;
131688
- Table *pTab = pColExpr->y.pTab;
131689
- assert( pTab!=0 );
131690131806
if( iCol<0 ) iCol = pTab->iPKey;
131691131807
zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
131692131808
}else if( pColExpr->op==TK_ID ){
131693131809
assert( !ExprHasProperty(pColExpr, EP_IntValue) );
131694131810
zName = pColExpr->u.zToken;
@@ -136959,26 +137075,15 @@
136959137075
goto trigger_cleanup;
136960137076
}
136961137077
pTab = sqlite3SrcListLookup(pParse, pTableName);
136962137078
if( !pTab ){
136963137079
/* The table does not exist. */
136964
- if( db->init.iDb==1 ){
136965
- /* Ticket #3810.
136966
- ** Normally, whenever a table is dropped, all associated triggers are
136967
- ** dropped too. But if a TEMP trigger is created on a non-TEMP table
136968
- ** and the table is dropped by a different database connection, the
136969
- ** trigger is not visible to the database connection that does the
136970
- ** drop so the trigger cannot be dropped. This results in an
136971
- ** "orphaned trigger" - a trigger whose associated table is missing.
136972
- */
136973
- db->init.orphanTrigger = 1;
136974
- }
136975
- goto trigger_cleanup;
137080
+ goto trigger_orphan_error;
136976137081
}
136977137082
if( IsVirtual(pTab) ){
136978137083
sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
136979
- goto trigger_cleanup;
137084
+ goto trigger_orphan_error;
136980137085
}
136981137086
136982137087
/* Check that the trigger name is not reserved and that no trigger of the
136983137088
** specified name exists */
136984137089
zName = sqlite3NameFromToken(db, pName);
@@ -137012,16 +137117,16 @@
137012137117
** of triggers.
137013137118
*/
137014137119
if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
137015137120
sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
137016137121
(tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
137017
- goto trigger_cleanup;
137122
+ goto trigger_orphan_error;
137018137123
}
137019137124
if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
137020137125
sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
137021137126
" trigger on table: %S", pTableName, 0);
137022
- goto trigger_cleanup;
137127
+ goto trigger_orphan_error;
137023137128
}
137024137129
137025137130
#ifndef SQLITE_OMIT_AUTHORIZATION
137026137131
if( !IN_RENAME_OBJECT ){
137027137132
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -137077,10 +137182,27 @@
137077137182
if( !pParse->pNewTrigger ){
137078137183
sqlite3DeleteTrigger(db, pTrigger);
137079137184
}else{
137080137185
assert( pParse->pNewTrigger==pTrigger );
137081137186
}
137187
+ return;
137188
+
137189
+trigger_orphan_error:
137190
+ if( db->init.iDb==1 ){
137191
+ /* Ticket #3810.
137192
+ ** Normally, whenever a table is dropped, all associated triggers are
137193
+ ** dropped too. But if a TEMP trigger is created on a non-TEMP table
137194
+ ** and the table is dropped by a different database connection, the
137195
+ ** trigger is not visible to the database connection that does the
137196
+ ** drop so the trigger cannot be dropped. This results in an
137197
+ ** "orphaned trigger" - a trigger whose associated table is missing.
137198
+ **
137199
+ ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df
137200
+ */
137201
+ db->init.orphanTrigger = 1;
137202
+ }
137203
+ goto trigger_cleanup;
137082137204
}
137083137205
137084137206
/*
137085137207
** This routine is called after all of the trigger actions have been parsed
137086137208
** in order to complete the process of building the trigger.
@@ -138664,10 +138786,12 @@
138664138786
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
138665138787
}
138666138788
138667138789
if( nChangeFrom==0 && HasRowid(pTab) ){
138668138790
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
138791
+ iEph = pParse->nTab++;
138792
+ addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet);
138669138793
}else{
138670138794
assert( pPk!=0 || HasRowid(pTab) );
138671138795
nPk = pPk ? pPk->nKeyCol : 0;
138672138796
iPk = pParse->nMem+1;
138673138797
pParse->nMem += nPk;
@@ -138755,13 +138879,14 @@
138755138879
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
138756138880
** mode, write the rowid into the FIFO. In either of the one-pass modes,
138757138881
** leave it in register regOldRowid. */
138758138882
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
138759138883
if( eOnePass==ONEPASS_OFF ){
138760
- /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */
138761138884
aRegIdx[nAllIdx] = ++pParse->nMem;
138762
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
138885
+ sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid);
138886
+ }else{
138887
+ if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen);
138763138888
}
138764138889
}else{
138765138890
/* Read the PK of the current row into an array of registers. In
138766138891
** ONEPASS_OFF mode, serialize the array into a record and store it in
138767138892
** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
@@ -138845,12 +138970,13 @@
138845138970
sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
138846138971
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0);
138847138972
VdbeCoverage(v);
138848138973
}
138849138974
}else{
138850
- labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
138851
- regOldRowid);
138975
+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
138976
+ labelContinue = sqlite3VdbeMakeLabel(pParse);
138977
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid);
138852138978
VdbeCoverage(v);
138853138979
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
138854138980
VdbeCoverage(v);
138855138981
}
138856138982
}
@@ -139096,15 +139222,13 @@
139096139222
if( eOnePass==ONEPASS_SINGLE ){
139097139223
/* Nothing to do at end-of-loop for a single-pass */
139098139224
}else if( eOnePass==ONEPASS_MULTI ){
139099139225
sqlite3VdbeResolveLabel(v, labelContinue);
139100139226
sqlite3WhereEnd(pWInfo);
139101
- }else if( pPk || nChangeFrom ){
139227
+ }else{
139102139228
sqlite3VdbeResolveLabel(v, labelContinue);
139103139229
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
139104
- }else{
139105
- sqlite3VdbeGoto(v, labelContinue);
139106139230
}
139107139231
sqlite3VdbeResolveLabel(v, labelBreak);
139108139232
139109139233
/* Update the sqlite_sequence table by storing the content of the
139110139234
** maximum rowid counter values recorded while inserting into
@@ -145894,10 +146018,11 @@
145894146018
** all terms of the WHERE clause.
145895146019
*/
145896146020
SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
145897146021
Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr);
145898146022
pWC->op = op;
146023
+ assert( pE2!=0 || pExpr==0 );
145899146024
if( pE2==0 ) return;
145900146025
if( pE2->op!=op ){
145901146026
whereClauseInsert(pWC, pExpr, 0);
145902146027
}else{
145903146028
sqlite3WhereSplit(pWC, pE2->pLeft, op);
@@ -146292,10 +146417,20 @@
146292146417
*/
146293146418
static void createMask(WhereMaskSet *pMaskSet, int iCursor){
146294146419
assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
146295146420
pMaskSet->ix[pMaskSet->n++] = iCursor;
146296146421
}
146422
+
146423
+/*
146424
+** If the right-hand branch of the expression is a TK_COLUMN, then return
146425
+** a pointer to the right-hand branch. Otherwise, return NULL.
146426
+*/
146427
+static Expr *whereRightSubexprIsColumn(Expr *p){
146428
+ p = sqlite3ExprSkipCollateAndLikely(p->pRight);
146429
+ if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p;
146430
+ return 0;
146431
+}
146297146432
146298146433
/*
146299146434
** Advance to the next WhereTerm that matches according to the criteria
146300146435
** established when the pScan object was initialized by whereScanInit().
146301146436
** Return NULL if there are no more matching WhereTerms.
@@ -146323,12 +146458,11 @@
146323146458
pScan->pIdxExpr,iCur)==0)
146324146459
&& (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
146325146460
){
146326146461
if( (pTerm->eOperator & WO_EQUIV)!=0
146327146462
&& pScan->nEquiv<ArraySize(pScan->aiCur)
146328
- && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op
146329
- ==TK_COLUMN
146463
+ && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0
146330146464
){
146331146465
int j;
146332146466
for(j=0; j<pScan->nEquiv; j++){
146333146467
if( pScan->aiCur[j]==pX->iTable
146334146468
&& pScan->aiColumn[j]==pX->iColumn ){
@@ -146520,11 +146654,12 @@
146520146654
int i;
146521146655
const char *zColl = pIdx->azColl[iCol];
146522146656
146523146657
for(i=0; i<pList->nExpr; i++){
146524146658
Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr);
146525
- if( p->op==TK_COLUMN
146659
+ if( ALWAYS(p!=0)
146660
+ && p->op==TK_COLUMN
146526146661
&& p->iColumn==pIdx->aiColumn[iCol]
146527146662
&& p->iTable==iBase
146528146663
){
146529146664
CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr);
146530146665
if( 0==sqlite3StrICmp(pColl->zName, zColl) ){
@@ -146584,10 +146719,11 @@
146584146719
** true. Note: The (p->iTable==iBase) part of this test may be false if the
146585146720
** current SELECT is a correlated sub-query.
146586146721
*/
146587146722
for(i=0; i<pDistinct->nExpr; i++){
146588146723
Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr);
146724
+ if( NEVER(p==0) ) continue;
146589146725
if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
146590146726
}
146591146727
146592146728
/* Loop through all indices on the table, checking each to see if it makes
146593146729
** the DISTINCT qualifier redundant. It does so if:
@@ -148498,13 +148634,13 @@
148498148634
LogEst rLogSize; /* Logarithm of table size */
148499148635
WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
148500148636
148501148637
pNew = pBuilder->pNew;
148502148638
if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
148503
- WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n",
148639
+ WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n",
148504148640
pProbe->pTable->zName,pProbe->zName,
148505
- pNew->u.btree.nEq, pNew->nSkip));
148641
+ pNew->u.btree.nEq, pNew->nSkip, pNew->rRun));
148506148642
148507148643
assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
148508148644
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
148509148645
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
148510148646
opMask = WO_LT|WO_LE;
@@ -148869,10 +149005,11 @@
148869149005
148870149006
if( pIndex->bUnordered ) return 0;
148871149007
if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
148872149008
for(ii=0; ii<pOB->nExpr; ii++){
148873149009
Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
149010
+ if( NEVER(pExpr==0) ) continue;
148874149011
if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
148875149012
if( pExpr->iColumn<0 ) return 1;
148876149013
for(jj=0; jj<pIndex->nKeyCol; jj++){
148877149014
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
148878149015
}
@@ -149847,10 +149984,11 @@
149847149984
** loops.
149848149985
*/
149849149986
for(i=0; i<nOrderBy; i++){
149850149987
if( MASKBIT(i) & obSat ) continue;
149851149988
pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
149989
+ if( NEVER(pOBExpr==0) ) continue;
149852149990
if( pOBExpr->op!=TK_COLUMN ) continue;
149853149991
if( pOBExpr->iTable!=iCur ) continue;
149854149992
pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
149855149993
~ready, eqOpMask, 0);
149856149994
if( pTerm==0 ) continue;
@@ -149973,10 +150111,11 @@
149973150111
for(i=0; bOnce && i<nOrderBy; i++){
149974150112
if( MASKBIT(i) & obSat ) continue;
149975150113
pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
149976150114
testcase( wctrlFlags & WHERE_GROUPBY );
149977150115
testcase( wctrlFlags & WHERE_DISTINCTBY );
150116
+ if( NEVER(pOBExpr==0) ) continue;
149978150117
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
149979150118
if( iColumn>=XN_ROWID ){
149980150119
if( pOBExpr->op!=TK_COLUMN ) continue;
149981150120
if( pOBExpr->iTable!=iCur ) continue;
149982150121
if( pOBExpr->iColumn!=iColumn ) continue;
@@ -150136,11 +150275,11 @@
150136150275
rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
150137150276
rSortCost = nRow + rScale + 16;
150138150277
150139150278
/* Multiple by log(M) where M is the number of output rows.
150140150279
** Use the LIMIT for M if it is smaller. Or if this sort is for
150141
- ** a DISTINT operator, M will be the number of distinct output
150280
+ ** a DISTINCT operator, M will be the number of distinct output
150142150281
** rows, so fudge it downwards a bit.
150143150282
*/
150144150283
if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
150145150284
nRow = pWInfo->iLimit;
150146150285
}else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){
@@ -194220,11 +194359,11 @@
194220194359
p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
194221194360
p->nEvent = p->nSegment = 0;
194222194361
geopolyAddSegments(p, p1, 1);
194223194362
geopolyAddSegments(p, p2, 2);
194224194363
pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
194225
- rX = pThisEvent->x==0.0 ? -1.0 : 0.0;
194364
+ rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0;
194226194365
memset(aOverlap, 0, sizeof(aOverlap));
194227194366
while( pThisEvent ){
194228194367
if( pThisEvent->x!=rX ){
194229194368
GeoSegment *pPrev = 0;
194230194369
int iMask = 0;
@@ -212129,11 +212268,11 @@
212129212268
Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */
212130212269
){
212131212270
int rc = SQLITE_OK; /* Return code */
212132212271
Fts5Bm25Data *p; /* Object to return */
212133212272
212134
- p = pApi->xGetAuxdata(pFts, 0);
212273
+ p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0);
212135212274
if( p==0 ){
212136212275
int nPhrase; /* Number of phrases in query */
212137212276
sqlite3_int64 nRow = 0; /* Number of rows in table */
212138212277
sqlite3_int64 nToken = 0; /* Number of tokens in table */
212139212278
sqlite3_int64 nByte; /* Bytes of space to allocate */
@@ -212203,11 +212342,11 @@
212203212342
int nVal, /* Number of values in apVal[] array */
212204212343
sqlite3_value **apVal /* Array of trailing arguments */
212205212344
){
212206212345
const double k1 = 1.2; /* Constant "k1" from BM25 formula */
212207212346
const double b = 0.75; /* Constant "b" from BM25 formula */
212208
- int rc = SQLITE_OK; /* Error code */
212347
+ int rc; /* Error code */
212209212348
double score = 0.0; /* SQL function return value */
212210212349
Fts5Bm25Data *pData; /* Values allocated/calculated once only */
212211212350
int i; /* Iterator variable */
212212212351
int nInst = 0; /* Value returned by xInstCount() */
212213212352
double D = 0.0; /* Total number of tokens in row */
@@ -212235,21 +212374,19 @@
212235212374
int nTok;
212236212375
rc = pApi->xColumnSize(pFts, -1, &nTok);
212237212376
D = (double)nTok;
212238212377
}
212239212378
212240
- /* Determine the BM25 score for the current row. */
212241
- for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){
212242
- score += pData->aIDF[i] * (
212243
- ( aFreq[i] * (k1 + 1.0) ) /
212244
- ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
212245
- );
212246
- }
212247
-
212248
- /* If no error has occurred, return the calculated score. Otherwise,
212249
- ** throw an SQL exception. */
212379
+ /* Determine and return the BM25 score for the current row. Or, if an
212380
+ ** error has occurred, throw an exception. */
212250212381
if( rc==SQLITE_OK ){
212382
+ for(i=0; i<pData->nPhrase; i++){
212383
+ score += pData->aIDF[i] * (
212384
+ ( aFreq[i] * (k1 + 1.0) ) /
212385
+ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
212386
+ );
212387
+ }
212251212388
sqlite3_result_double(pCtx, -1.0 * score);
212252212389
}else{
212253212390
sqlite3_result_error_code(pCtx, rc);
212254212391
}
212255212392
}
@@ -223384,10 +223521,11 @@
223384223521
cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n);
223385223522
}
223386223523
}else{
223387223524
poslist.n = 0;
223388223525
fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
223526
+ fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0");
223389223527
while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
223390223528
int iCol = FTS5_POS2COLUMN(iPos);
223391223529
int iTokOff = FTS5_POS2OFFSET(iPos);
223392223530
cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n);
223393223531
}
@@ -226679,11 +226817,11 @@
226679226817
int nArg, /* Number of args */
226680226818
sqlite3_value **apUnused /* Function arguments */
226681226819
){
226682226820
assert( nArg==0 );
226683226821
UNUSED_PARAM2(nArg, apUnused);
226684
- sqlite3_result_text(pCtx, "fts5: 2020-10-26 16:22:31 80eba105d6d1b49ba8ca2ad4e14ddec2de0bdc2f6686c2f8a1c1d24fc1fe846f", -1, SQLITE_TRANSIENT);
226822
+ sqlite3_result_text(pCtx, "fts5: 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -1, SQLITE_TRANSIENT);
226685226823
}
226686226824
226687226825
/*
226688226826
** Return true if zName is the extension on one of the shadow tables used
226689226827
** by this module.
@@ -229252,17 +229390,18 @@
229252229390
229253229391
/*
229254229392
** Allocate a trigram tokenizer.
229255229393
*/
229256229394
static int fts5TriCreate(
229257
- void *pCtx,
229395
+ void *pUnused,
229258229396
const char **azArg,
229259229397
int nArg,
229260229398
Fts5Tokenizer **ppOut
229261229399
){
229262229400
int rc = SQLITE_OK;
229263229401
TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
229402
+ UNUSED_PARAM(pUnused);
229264229403
if( pNew==0 ){
229265229404
rc = SQLITE_NOMEM;
229266229405
}else{
229267229406
int i;
229268229407
pNew->bFold = 1;
@@ -229291,11 +229430,11 @@
229291229430
** Trigram tokenizer tokenize routine.
229292229431
*/
229293229432
static int fts5TriTokenize(
229294229433
Fts5Tokenizer *pTok,
229295229434
void *pCtx,
229296
- int flags,
229435
+ int unusedFlags,
229297229436
const char *pText, int nText,
229298229437
int (*xToken)(void*, int, const char*, int, int, int)
229299229438
){
229300229439
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
229301229440
int rc = SQLITE_OK;
@@ -229302,10 +229441,11 @@
229302229441
char aBuf[32];
229303229442
const unsigned char *zIn = (const unsigned char*)pText;
229304229443
const unsigned char *zEof = &zIn[nText];
229305229444
u32 iCode;
229306229445
229446
+ UNUSED_PARAM(unusedFlags);
229307229447
while( 1 ){
229308229448
char *zOut = aBuf;
229309229449
int iStart = zIn - (const unsigned char*)pText;
229310229450
const unsigned char *zNext;
229311229451
@@ -230164,10 +230304,11 @@
230164230304
}
230165230305
iTbl++;
230166230306
}
230167230307
aAscii[0] = 0; /* 0x00 is never a token character */
230168230308
}
230309
+
230169230310
230170230311
/*
230171230312
** 2015 May 30
230172230313
**
230173230314
** The author disclaims copyright to this source code. In place of
@@ -231602,12 +231743,12 @@
231602231743
}
231603231744
#endif /* SQLITE_CORE */
231604231745
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
231605231746
231606231747
/************** End of stmt.c ************************************************/
231607
-#if __LINE__!=231607
231748
+#if __LINE__!=231748
231608231749
#undef SQLITE_SOURCE_ID
231609
-#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692alt2"
231750
+#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2"
231610231751
#endif
231611231752
/* Return the source-id for this library */
231612231753
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
231613231754
/************************** End of sqlite3.c ******************************/
231614231755
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1171,11 +1171,11 @@
1171 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1172 ** [sqlite_version()] and [sqlite_source_id()].
1173 */
1174 #define SQLITE_VERSION "3.34.0"
1175 #define SQLITE_VERSION_NUMBER 3034000
1176 #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d"
1177
1178 /*
1179 ** CAPI3REF: Run-Time Library Version Numbers
1180 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1181 **
@@ -1550,10 +1550,11 @@
1550 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
1551 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
1552 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
1553 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
1554 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
 
1555 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
1556 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
1557 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
1558 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
1559 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -7238,11 +7239,11 @@
7238 ** CAPI3REF: Determine the transaction state of a database
7239 ** METHOD: sqlite3
7240 **
7241 ** ^The sqlite3_txn_state(D,S) interface returns the current
7242 ** [transaction state] of schema S in database connection D. ^If S is NULL,
7243 ** then the highest transaction state of any schema on databse connection D
7244 ** is returned. Transaction states are (in order of lowest to highest):
7245 ** <ol>
7246 ** <li value="0"> SQLITE_TXN_NONE
7247 ** <li value="1"> SQLITE_TXN_READ
7248 ** <li value="2"> SQLITE_TXN_WRITE
@@ -8785,11 +8786,10 @@
8785 */
8786 #define SQLITE_TESTCTRL_FIRST 5
8787 #define SQLITE_TESTCTRL_PRNG_SAVE 5
8788 #define SQLITE_TESTCTRL_PRNG_RESTORE 6
8789 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
8790 #define SQLITE_TESTCTRL_SEEK_COUNT 7
8791 #define SQLITE_TESTCTRL_BITVEC_TEST 8
8792 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
8793 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
8794 #define SQLITE_TESTCTRL_PENDING_BYTE 11
8795 #define SQLITE_TESTCTRL_ASSERT 12
@@ -8810,11 +8810,12 @@
8810 #define SQLITE_TESTCTRL_IMPOSTER 25
8811 #define SQLITE_TESTCTRL_PARSER_COVERAGE 26
8812 #define SQLITE_TESTCTRL_RESULT_INTREAL 27
8813 #define SQLITE_TESTCTRL_PRNG_SEED 28
8814 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
8815 #define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
 
8816
8817 /*
8818 ** CAPI3REF: SQL Keyword Checking
8819 **
8820 ** These routines provide access to the set of SQL language keywords
@@ -28182,16 +28183,19 @@
28182 EnableLookaside;
28183 }
28184 }
28185
28186 /*
28187 ** Take actions at the end of an API call to indicate an OOM error
28188 */
28189 static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
28190 sqlite3OomClear(db);
28191 sqlite3Error(db, SQLITE_NOMEM);
28192 return SQLITE_NOMEM_BKPT;
 
 
 
28193 }
28194
28195 /*
28196 ** This function must be called before exiting any API function (i.e.
28197 ** returning control to the user) that has called sqlite3_malloc or
@@ -28209,12 +28213,12 @@
28209 ** Otherwise the read (and possible write) of db->mallocFailed
28210 ** is unsafe, as is the call to sqlite3Error().
28211 */
28212 assert( db!=0 );
28213 assert( sqlite3_mutex_held(db->mutex) );
28214 if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
28215 return apiOomError(db);
28216 }
28217 return rc & db->errMask;
28218 }
28219
28220 /************** End of malloc.c **********************************************/
@@ -37003,11 +37007,28 @@
37003
37004 got = seekAndRead(pFile, offset, pBuf, amt);
37005 if( got==amt ){
37006 return SQLITE_OK;
37007 }else if( got<0 ){
37008 /* lastErrno set by seekAndRead */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37009 return SQLITE_IOERR_READ;
37010 }else{
37011 storeLastErrno(pFile, 0); /* not a system error */
37012 /* Unread parts of the buffer must be zero-filled */
37013 memset(&((char*)pBuf)[got], 0, amt-got);
@@ -38535,11 +38556,11 @@
38535 if( bUnlock ){
38536 rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
38537 if( rc==SQLITE_OK ){
38538 memset(&aLock[ofst], 0, sizeof(int)*n);
38539 }
38540 }else if( p->sharedMask & (1<<ofst) ){
38541 assert( n==1 && aLock[ofst]>1 );
38542 aLock[ofst]--;
38543 }
38544
38545 /* Undo the local locks */
@@ -38568,11 +38589,11 @@
38568 /* Make sure no sibling connections hold locks that will block this
38569 ** lock. If any do, return SQLITE_BUSY right away. */
38570 int ii;
38571 for(ii=ofst; ii<ofst+n; ii++){
38572 assert( (p->sharedMask & mask)==0 );
38573 if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){
38574 rc = SQLITE_BUSY;
38575 break;
38576 }
38577 }
38578
@@ -39964,19 +39985,39 @@
39964 }
39965 return SQLITE_OK;
39966 }
39967
39968 /*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39969 **
 
 
39970 */
39971 static int mkFullPathname(
39972 const char *zPath, /* Input path */
39973 char *zOut, /* Output buffer */
39974 int nOut /* Allocated size of buffer zOut */
39975 ){
39976 int nPath = sqlite3Strlen30(zPath);
39977 int iOff = 0;
 
39978 if( zPath[0]!='/' ){
39979 if( osGetcwd(zOut, nOut-2)==0 ){
39980 return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
39981 }
39982 iOff = sqlite3Strlen30(zOut);
@@ -39987,10 +40028,45 @@
39987 ** even if it returns an error. */
39988 zOut[iOff] = '\0';
39989 return SQLITE_CANTOPEN_BKPT;
39990 }
39991 sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39992 return SQLITE_OK;
39993 }
39994
39995 /*
39996 ** Turn a relative pathname into a full pathname. The relative path
@@ -54326,10 +54402,11 @@
54326 sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */
54327 char *zSuperJournal = 0; /* Contents of super-journal file */
54328 i64 nSuperJournal; /* Size of super-journal file */
54329 char *zJournal; /* Pointer to one journal within MJ file */
54330 char *zSuperPtr; /* Space to hold super-journal filename */
 
54331 int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
54332
54333 /* Allocate space for both the pJournal and pSuper file descriptors.
54334 ** If successful, open the super-journal file for reading.
54335 */
@@ -54350,15 +54427,17 @@
54350 ** files extracted from regular rollback-journals.
54351 */
54352 rc = sqlite3OsFileSize(pSuper, &nSuperJournal);
54353 if( rc!=SQLITE_OK ) goto delsuper_out;
54354 nSuperPtr = pVfs->mxPathname+1;
54355 zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2);
54356 if( !zSuperJournal ){
54357 rc = SQLITE_NOMEM_BKPT;
54358 goto delsuper_out;
54359 }
 
 
54360 zSuperPtr = &zSuperJournal[nSuperJournal+2];
54361 rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0);
54362 if( rc!=SQLITE_OK ) goto delsuper_out;
54363 zSuperJournal[nSuperJournal] = 0;
54364 zSuperJournal[nSuperJournal+1] = 0;
@@ -54402,11 +54481,11 @@
54402
54403 sqlite3OsClose(pSuper);
54404 rc = sqlite3OsDelete(pVfs, zSuper, 0);
54405
54406 delsuper_out:
54407 sqlite3_free(zSuperJournal);
54408 if( pSuper ){
54409 sqlite3OsClose(pSuper);
54410 assert( !isOpen(pJournal) );
54411 sqlite3_free(pSuper);
54412 }
@@ -54740,11 +54819,15 @@
54740 ** in case this has happened, clear the changeCountDone flag now.
54741 */
54742 pPager->changeCountDone = pPager->tempFile;
54743
54744 if( rc==SQLITE_OK ){
54745 zSuper = pPager->pTmpSpace;
 
 
 
 
54746 rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
54747 testcase( rc!=SQLITE_OK );
54748 }
54749 if( rc==SQLITE_OK
54750 && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
@@ -54757,10 +54840,12 @@
54757 }
54758 if( rc==SQLITE_OK && zSuper[0] && res ){
54759 /* If there was a super-journal and this routine will return success,
54760 ** see if it is possible to delete the super-journal.
54761 */
 
 
54762 rc = pager_delsuper(pPager, zSuper);
54763 testcase( rc!=SQLITE_OK );
54764 }
54765 if( isHot && nPlayback ){
54766 sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s",
@@ -64746,11 +64831,11 @@
64746 #define hasReadConflicts(a, b) 0
64747 #endif
64748
64749 #ifdef SQLITE_DEBUG
64750 /*
64751 ** Return an reset the seek counter for a Btree object.
64752 */
64753 SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){
64754 u64 n = pBt->nSeek;
64755 pBt->nSeek = 0;
64756 return n;
@@ -82192,13 +82277,16 @@
82192 ** equal to, or greater than the second (double).
82193 */
82194 static int sqlite3IntFloatCompare(i64 i, double r){
82195 if( sizeof(LONGDOUBLE_TYPE)>8 ){
82196 LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
 
 
 
82197 if( x<r ) return -1;
82198 if( x>r ) return +1;
82199 return 0;
82200 }else{
82201 i64 y;
82202 double s;
82203 if( r<-9223372036854775808.0 ) return +1;
82204 if( r>=9223372036854775808.0 ) return -1;
@@ -89388,11 +89476,11 @@
89388 assert( rc==SQLITE_OK );
89389 break;
89390 }
89391
89392
89393 /* Opcode: OpenEphemeral P1 P2 * P4 P5
89394 ** Synopsis: nColumn=P2
89395 **
89396 ** Open a new cursor P1 to a transient table.
89397 ** The cursor is always opened read/write even if
89398 ** the main database is read-only. The ephemeral
@@ -89408,10 +89496,14 @@
89408 **
89409 ** The P5 parameter can be a mask of the BTREE_* flags defined
89410 ** in btree.h. These flags control aspects of the operation of
89411 ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
89412 ** added automatically.
 
 
 
 
89413 */
89414 /* Opcode: OpenAutoindex P1 P2 * P4 *
89415 ** Synopsis: nColumn=P2
89416 **
89417 ** This opcode works the same as OP_OpenEphemeral. It has a
@@ -89430,10 +89522,19 @@
89430 SQLITE_OPEN_EXCLUSIVE |
89431 SQLITE_OPEN_DELETEONCLOSE |
89432 SQLITE_OPEN_TRANSIENT_DB;
89433 assert( pOp->p1>=0 );
89434 assert( pOp->p2>=0 );
 
 
 
 
 
 
 
 
 
89435 pCx = p->apCsr[pOp->p1];
89436 if( pCx && pCx->pBtx ){
89437 /* If the ephermeral table is already open, erase all existing content
89438 ** so that the table is empty again, rather than creating a new table. */
89439 assert( pCx->isEphemeral );
@@ -90589,11 +90690,11 @@
90589 if( pOp->p5 & OPFLAG_ISNOOP ) break;
90590 #endif
90591
90592 if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
90593 if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
90594 assert( pData->flags & (MEM_Blob|MEM_Str) );
90595 x.pData = pData->z;
90596 x.nData = pData->n;
90597 seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
90598 if( pData->flags & MEM_Zero ){
90599 x.nZero = pData->u.nZero;
@@ -93644,11 +93745,15 @@
93644
93645 /* If we reach this point, it means that execution is finished with
93646 ** an error of some kind.
93647 */
93648 abort_due_to_error:
93649 if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT;
 
 
 
 
93650 assert( rc );
93651 if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
93652 sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
93653 }
93654 p->rc = rc;
@@ -99381,10 +99486,11 @@
99381 for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
99382 int iCol = -1;
99383 Expr *pE, *pDup;
99384 if( pItem->done ) continue;
99385 pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
 
99386 if( sqlite3ExprIsInteger(pE, &iCol) ){
99387 if( iCol<=0 || iCol>pEList->nExpr ){
99388 resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
99389 return 1;
99390 }
@@ -99560,10 +99666,11 @@
99560 nResult = pSelect->pEList->nExpr;
99561 pParse = pNC->pParse;
99562 for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
99563 Expr *pE = pItem->pExpr;
99564 Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
 
99565 if( zType[0]!='G' ){
99566 iCol = resolveAsName(pParse, pSelect->pEList, pE2);
99567 if( iCol>0 ){
99568 /* If an AS-name match is found, mark this ORDER BY column as being
99569 ** a copy of the iCol-th result-set column. The subsequent call to
@@ -103679,10 +103786,11 @@
103679 ** register iReg. The caller must ensure that iReg already contains
103680 ** the correct value for the expression.
103681 */
103682 static void exprToRegister(Expr *pExpr, int iReg){
103683 Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
 
103684 p->op2 = p->op;
103685 p->op = TK_REGISTER;
103686 p->iTable = iReg;
103687 ExprClearProperty(p, EP_Skip);
103688 }
@@ -104666,10 +104774,11 @@
104666 */
104667 SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
104668 int r2;
104669 pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
104670 if( ConstFactorOk(pParse)
 
104671 && pExpr->op!=TK_REGISTER
104672 && sqlite3ExprIsConstantNotJoin(pExpr)
104673 ){
104674 *pReg = 0;
104675 r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -119424,10 +119533,12 @@
119424 VFUNCTION(total_changes, 0, 0, 0, total_changes ),
119425 FUNCTION(replace, 3, 0, 0, replaceFunc ),
119426 FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
119427 FUNCTION(substr, 2, 0, 0, substrFunc ),
119428 FUNCTION(substr, 3, 0, 0, substrFunc ),
 
 
119429 WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
119430 WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
119431 WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
119432 WAGGREGATE(count, 0,0,0, countStep,
119433 countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
@@ -124310,10 +124421,12 @@
124310 /* Version 3.32.0 and later */
124311 char *(*create_filename)(const char*,const char*,const char*,
124312 int,const char**);
124313 void (*free_filename)(char*);
124314 sqlite3_file *(*database_file_object)(const char*);
 
 
124315 };
124316
124317 /*
124318 ** This is the function signature used for all extension entry points. It
124319 ** is also defined in the file "loadext.c".
@@ -124614,10 +124727,12 @@
124614 #define sqlite3_filename_wal sqlite3_api->filename_wal
124615 /* Version 3.32.0 and later */
124616 #define sqlite3_create_filename sqlite3_api->create_filename
124617 #define sqlite3_free_filename sqlite3_api->free_filename
124618 #define sqlite3_database_file_object sqlite3_api->database_file_object
 
 
124619 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
124620
124621 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
124622 /* This case when the file really is being compiled as a loadable
124623 ** extension */
@@ -125096,10 +125211,12 @@
125096 sqlite3_filename_wal,
125097 /* Version 3.32.0 and later */
125098 sqlite3_create_filename,
125099 sqlite3_free_filename,
125100 sqlite3_database_file_object,
 
 
125101 };
125102
125103 /* True if x is the directory separator character
125104 */
125105 #if SQLITE_OS_WIN
@@ -131654,10 +131771,11 @@
131654 Column *aCol, *pCol; /* For looping over result columns */
131655 int nCol; /* Number of columns in the result set */
131656 char *zName; /* Column name */
131657 int nName; /* Size of name in zName[] */
131658 Hash ht; /* Hash table of column names */
 
131659
131660 sqlite3HashInit(&ht);
131661 if( pEList ){
131662 nCol = pEList->nExpr;
131663 aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
@@ -131676,19 +131794,17 @@
131676 */
131677 if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){
131678 /* If the column contains an "AS <name>" phrase, use <name> as the name */
131679 }else{
131680 Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
131681 while( pColExpr->op==TK_DOT ){
131682 pColExpr = pColExpr->pRight;
131683 assert( pColExpr!=0 );
131684 }
131685 if( pColExpr->op==TK_COLUMN ){
131686 /* For columns use the column name name */
131687 int iCol = pColExpr->iColumn;
131688 Table *pTab = pColExpr->y.pTab;
131689 assert( pTab!=0 );
131690 if( iCol<0 ) iCol = pTab->iPKey;
131691 zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
131692 }else if( pColExpr->op==TK_ID ){
131693 assert( !ExprHasProperty(pColExpr, EP_IntValue) );
131694 zName = pColExpr->u.zToken;
@@ -136959,26 +137075,15 @@
136959 goto trigger_cleanup;
136960 }
136961 pTab = sqlite3SrcListLookup(pParse, pTableName);
136962 if( !pTab ){
136963 /* The table does not exist. */
136964 if( db->init.iDb==1 ){
136965 /* Ticket #3810.
136966 ** Normally, whenever a table is dropped, all associated triggers are
136967 ** dropped too. But if a TEMP trigger is created on a non-TEMP table
136968 ** and the table is dropped by a different database connection, the
136969 ** trigger is not visible to the database connection that does the
136970 ** drop so the trigger cannot be dropped. This results in an
136971 ** "orphaned trigger" - a trigger whose associated table is missing.
136972 */
136973 db->init.orphanTrigger = 1;
136974 }
136975 goto trigger_cleanup;
136976 }
136977 if( IsVirtual(pTab) ){
136978 sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
136979 goto trigger_cleanup;
136980 }
136981
136982 /* Check that the trigger name is not reserved and that no trigger of the
136983 ** specified name exists */
136984 zName = sqlite3NameFromToken(db, pName);
@@ -137012,16 +137117,16 @@
137012 ** of triggers.
137013 */
137014 if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
137015 sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
137016 (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
137017 goto trigger_cleanup;
137018 }
137019 if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
137020 sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
137021 " trigger on table: %S", pTableName, 0);
137022 goto trigger_cleanup;
137023 }
137024
137025 #ifndef SQLITE_OMIT_AUTHORIZATION
137026 if( !IN_RENAME_OBJECT ){
137027 int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -137077,10 +137182,27 @@
137077 if( !pParse->pNewTrigger ){
137078 sqlite3DeleteTrigger(db, pTrigger);
137079 }else{
137080 assert( pParse->pNewTrigger==pTrigger );
137081 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137082 }
137083
137084 /*
137085 ** This routine is called after all of the trigger actions have been parsed
137086 ** in order to complete the process of building the trigger.
@@ -138664,10 +138786,12 @@
138664 sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
138665 }
138666
138667 if( nChangeFrom==0 && HasRowid(pTab) ){
138668 sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
 
 
138669 }else{
138670 assert( pPk!=0 || HasRowid(pTab) );
138671 nPk = pPk ? pPk->nKeyCol : 0;
138672 iPk = pParse->nMem+1;
138673 pParse->nMem += nPk;
@@ -138755,13 +138879,14 @@
138755 /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
138756 ** mode, write the rowid into the FIFO. In either of the one-pass modes,
138757 ** leave it in register regOldRowid. */
138758 sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
138759 if( eOnePass==ONEPASS_OFF ){
138760 /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */
138761 aRegIdx[nAllIdx] = ++pParse->nMem;
138762 sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
 
 
138763 }
138764 }else{
138765 /* Read the PK of the current row into an array of registers. In
138766 ** ONEPASS_OFF mode, serialize the array into a record and store it in
138767 ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
@@ -138845,12 +138970,13 @@
138845 sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
138846 sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0);
138847 VdbeCoverage(v);
138848 }
138849 }else{
138850 labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
138851 regOldRowid);
 
138852 VdbeCoverage(v);
138853 sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
138854 VdbeCoverage(v);
138855 }
138856 }
@@ -139096,15 +139222,13 @@
139096 if( eOnePass==ONEPASS_SINGLE ){
139097 /* Nothing to do at end-of-loop for a single-pass */
139098 }else if( eOnePass==ONEPASS_MULTI ){
139099 sqlite3VdbeResolveLabel(v, labelContinue);
139100 sqlite3WhereEnd(pWInfo);
139101 }else if( pPk || nChangeFrom ){
139102 sqlite3VdbeResolveLabel(v, labelContinue);
139103 sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
139104 }else{
139105 sqlite3VdbeGoto(v, labelContinue);
139106 }
139107 sqlite3VdbeResolveLabel(v, labelBreak);
139108
139109 /* Update the sqlite_sequence table by storing the content of the
139110 ** maximum rowid counter values recorded while inserting into
@@ -145894,10 +146018,11 @@
145894 ** all terms of the WHERE clause.
145895 */
145896 SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
145897 Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr);
145898 pWC->op = op;
 
145899 if( pE2==0 ) return;
145900 if( pE2->op!=op ){
145901 whereClauseInsert(pWC, pExpr, 0);
145902 }else{
145903 sqlite3WhereSplit(pWC, pE2->pLeft, op);
@@ -146292,10 +146417,20 @@
146292 */
146293 static void createMask(WhereMaskSet *pMaskSet, int iCursor){
146294 assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
146295 pMaskSet->ix[pMaskSet->n++] = iCursor;
146296 }
 
 
 
 
 
 
 
 
 
 
146297
146298 /*
146299 ** Advance to the next WhereTerm that matches according to the criteria
146300 ** established when the pScan object was initialized by whereScanInit().
146301 ** Return NULL if there are no more matching WhereTerms.
@@ -146323,12 +146458,11 @@
146323 pScan->pIdxExpr,iCur)==0)
146324 && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
146325 ){
146326 if( (pTerm->eOperator & WO_EQUIV)!=0
146327 && pScan->nEquiv<ArraySize(pScan->aiCur)
146328 && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op
146329 ==TK_COLUMN
146330 ){
146331 int j;
146332 for(j=0; j<pScan->nEquiv; j++){
146333 if( pScan->aiCur[j]==pX->iTable
146334 && pScan->aiColumn[j]==pX->iColumn ){
@@ -146520,11 +146654,12 @@
146520 int i;
146521 const char *zColl = pIdx->azColl[iCol];
146522
146523 for(i=0; i<pList->nExpr; i++){
146524 Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr);
146525 if( p->op==TK_COLUMN
 
146526 && p->iColumn==pIdx->aiColumn[iCol]
146527 && p->iTable==iBase
146528 ){
146529 CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr);
146530 if( 0==sqlite3StrICmp(pColl->zName, zColl) ){
@@ -146584,10 +146719,11 @@
146584 ** true. Note: The (p->iTable==iBase) part of this test may be false if the
146585 ** current SELECT is a correlated sub-query.
146586 */
146587 for(i=0; i<pDistinct->nExpr; i++){
146588 Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr);
 
146589 if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
146590 }
146591
146592 /* Loop through all indices on the table, checking each to see if it makes
146593 ** the DISTINCT qualifier redundant. It does so if:
@@ -148498,13 +148634,13 @@
148498 LogEst rLogSize; /* Logarithm of table size */
148499 WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
148500
148501 pNew = pBuilder->pNew;
148502 if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
148503 WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n",
148504 pProbe->pTable->zName,pProbe->zName,
148505 pNew->u.btree.nEq, pNew->nSkip));
148506
148507 assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
148508 assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
148509 if( pNew->wsFlags & WHERE_BTM_LIMIT ){
148510 opMask = WO_LT|WO_LE;
@@ -148869,10 +149005,11 @@
148869
148870 if( pIndex->bUnordered ) return 0;
148871 if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
148872 for(ii=0; ii<pOB->nExpr; ii++){
148873 Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
 
148874 if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
148875 if( pExpr->iColumn<0 ) return 1;
148876 for(jj=0; jj<pIndex->nKeyCol; jj++){
148877 if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
148878 }
@@ -149847,10 +149984,11 @@
149847 ** loops.
149848 */
149849 for(i=0; i<nOrderBy; i++){
149850 if( MASKBIT(i) & obSat ) continue;
149851 pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
 
149852 if( pOBExpr->op!=TK_COLUMN ) continue;
149853 if( pOBExpr->iTable!=iCur ) continue;
149854 pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
149855 ~ready, eqOpMask, 0);
149856 if( pTerm==0 ) continue;
@@ -149973,10 +150111,11 @@
149973 for(i=0; bOnce && i<nOrderBy; i++){
149974 if( MASKBIT(i) & obSat ) continue;
149975 pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
149976 testcase( wctrlFlags & WHERE_GROUPBY );
149977 testcase( wctrlFlags & WHERE_DISTINCTBY );
 
149978 if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
149979 if( iColumn>=XN_ROWID ){
149980 if( pOBExpr->op!=TK_COLUMN ) continue;
149981 if( pOBExpr->iTable!=iCur ) continue;
149982 if( pOBExpr->iColumn!=iColumn ) continue;
@@ -150136,11 +150275,11 @@
150136 rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
150137 rSortCost = nRow + rScale + 16;
150138
150139 /* Multiple by log(M) where M is the number of output rows.
150140 ** Use the LIMIT for M if it is smaller. Or if this sort is for
150141 ** a DISTINT operator, M will be the number of distinct output
150142 ** rows, so fudge it downwards a bit.
150143 */
150144 if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
150145 nRow = pWInfo->iLimit;
150146 }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){
@@ -194220,11 +194359,11 @@
194220 p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
194221 p->nEvent = p->nSegment = 0;
194222 geopolyAddSegments(p, p1, 1);
194223 geopolyAddSegments(p, p2, 2);
194224 pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
194225 rX = pThisEvent->x==0.0 ? -1.0 : 0.0;
194226 memset(aOverlap, 0, sizeof(aOverlap));
194227 while( pThisEvent ){
194228 if( pThisEvent->x!=rX ){
194229 GeoSegment *pPrev = 0;
194230 int iMask = 0;
@@ -212129,11 +212268,11 @@
212129 Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */
212130 ){
212131 int rc = SQLITE_OK; /* Return code */
212132 Fts5Bm25Data *p; /* Object to return */
212133
212134 p = pApi->xGetAuxdata(pFts, 0);
212135 if( p==0 ){
212136 int nPhrase; /* Number of phrases in query */
212137 sqlite3_int64 nRow = 0; /* Number of rows in table */
212138 sqlite3_int64 nToken = 0; /* Number of tokens in table */
212139 sqlite3_int64 nByte; /* Bytes of space to allocate */
@@ -212203,11 +212342,11 @@
212203 int nVal, /* Number of values in apVal[] array */
212204 sqlite3_value **apVal /* Array of trailing arguments */
212205 ){
212206 const double k1 = 1.2; /* Constant "k1" from BM25 formula */
212207 const double b = 0.75; /* Constant "b" from BM25 formula */
212208 int rc = SQLITE_OK; /* Error code */
212209 double score = 0.0; /* SQL function return value */
212210 Fts5Bm25Data *pData; /* Values allocated/calculated once only */
212211 int i; /* Iterator variable */
212212 int nInst = 0; /* Value returned by xInstCount() */
212213 double D = 0.0; /* Total number of tokens in row */
@@ -212235,21 +212374,19 @@
212235 int nTok;
212236 rc = pApi->xColumnSize(pFts, -1, &nTok);
212237 D = (double)nTok;
212238 }
212239
212240 /* Determine the BM25 score for the current row. */
212241 for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){
212242 score += pData->aIDF[i] * (
212243 ( aFreq[i] * (k1 + 1.0) ) /
212244 ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
212245 );
212246 }
212247
212248 /* If no error has occurred, return the calculated score. Otherwise,
212249 ** throw an SQL exception. */
212250 if( rc==SQLITE_OK ){
 
 
 
 
 
 
212251 sqlite3_result_double(pCtx, -1.0 * score);
212252 }else{
212253 sqlite3_result_error_code(pCtx, rc);
212254 }
212255 }
@@ -223384,10 +223521,11 @@
223384 cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n);
223385 }
223386 }else{
223387 poslist.n = 0;
223388 fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
 
223389 while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
223390 int iCol = FTS5_POS2COLUMN(iPos);
223391 int iTokOff = FTS5_POS2OFFSET(iPos);
223392 cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n);
223393 }
@@ -226679,11 +226817,11 @@
226679 int nArg, /* Number of args */
226680 sqlite3_value **apUnused /* Function arguments */
226681 ){
226682 assert( nArg==0 );
226683 UNUSED_PARAM2(nArg, apUnused);
226684 sqlite3_result_text(pCtx, "fts5: 2020-10-26 16:22:31 80eba105d6d1b49ba8ca2ad4e14ddec2de0bdc2f6686c2f8a1c1d24fc1fe846f", -1, SQLITE_TRANSIENT);
226685 }
226686
226687 /*
226688 ** Return true if zName is the extension on one of the shadow tables used
226689 ** by this module.
@@ -229252,17 +229390,18 @@
229252
229253 /*
229254 ** Allocate a trigram tokenizer.
229255 */
229256 static int fts5TriCreate(
229257 void *pCtx,
229258 const char **azArg,
229259 int nArg,
229260 Fts5Tokenizer **ppOut
229261 ){
229262 int rc = SQLITE_OK;
229263 TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
 
229264 if( pNew==0 ){
229265 rc = SQLITE_NOMEM;
229266 }else{
229267 int i;
229268 pNew->bFold = 1;
@@ -229291,11 +229430,11 @@
229291 ** Trigram tokenizer tokenize routine.
229292 */
229293 static int fts5TriTokenize(
229294 Fts5Tokenizer *pTok,
229295 void *pCtx,
229296 int flags,
229297 const char *pText, int nText,
229298 int (*xToken)(void*, int, const char*, int, int, int)
229299 ){
229300 TrigramTokenizer *p = (TrigramTokenizer*)pTok;
229301 int rc = SQLITE_OK;
@@ -229302,10 +229441,11 @@
229302 char aBuf[32];
229303 const unsigned char *zIn = (const unsigned char*)pText;
229304 const unsigned char *zEof = &zIn[nText];
229305 u32 iCode;
229306
 
229307 while( 1 ){
229308 char *zOut = aBuf;
229309 int iStart = zIn - (const unsigned char*)pText;
229310 const unsigned char *zNext;
229311
@@ -230164,10 +230304,11 @@
230164 }
230165 iTbl++;
230166 }
230167 aAscii[0] = 0; /* 0x00 is never a token character */
230168 }
 
230169
230170 /*
230171 ** 2015 May 30
230172 **
230173 ** The author disclaims copyright to this source code. In place of
@@ -231602,12 +231743,12 @@
231602 }
231603 #endif /* SQLITE_CORE */
231604 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
231605
231606 /************** End of stmt.c ************************************************/
231607 #if __LINE__!=231607
231608 #undef SQLITE_SOURCE_ID
231609 #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692alt2"
231610 #endif
231611 /* Return the source-id for this library */
231612 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
231613 /************************** End of sqlite3.c ******************************/
231614
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1171,11 +1171,11 @@
1171 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1172 ** [sqlite_version()] and [sqlite_source_id()].
1173 */
1174 #define SQLITE_VERSION "3.34.0"
1175 #define SQLITE_VERSION_NUMBER 3034000
1176 #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
1177
1178 /*
1179 ** CAPI3REF: Run-Time Library Version Numbers
1180 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1181 **
@@ -1550,10 +1550,11 @@
1550 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
1551 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
1552 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
1553 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
1554 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
1555 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
1556 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
1557 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
1558 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
1559 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
1560 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -7238,11 +7239,11 @@
7239 ** CAPI3REF: Determine the transaction state of a database
7240 ** METHOD: sqlite3
7241 **
7242 ** ^The sqlite3_txn_state(D,S) interface returns the current
7243 ** [transaction state] of schema S in database connection D. ^If S is NULL,
7244 ** then the highest transaction state of any schema on database connection D
7245 ** is returned. Transaction states are (in order of lowest to highest):
7246 ** <ol>
7247 ** <li value="0"> SQLITE_TXN_NONE
7248 ** <li value="1"> SQLITE_TXN_READ
7249 ** <li value="2"> SQLITE_TXN_WRITE
@@ -8785,11 +8786,10 @@
8786 */
8787 #define SQLITE_TESTCTRL_FIRST 5
8788 #define SQLITE_TESTCTRL_PRNG_SAVE 5
8789 #define SQLITE_TESTCTRL_PRNG_RESTORE 6
8790 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
 
8791 #define SQLITE_TESTCTRL_BITVEC_TEST 8
8792 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
8793 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
8794 #define SQLITE_TESTCTRL_PENDING_BYTE 11
8795 #define SQLITE_TESTCTRL_ASSERT 12
@@ -8810,11 +8810,12 @@
8810 #define SQLITE_TESTCTRL_IMPOSTER 25
8811 #define SQLITE_TESTCTRL_PARSER_COVERAGE 26
8812 #define SQLITE_TESTCTRL_RESULT_INTREAL 27
8813 #define SQLITE_TESTCTRL_PRNG_SEED 28
8814 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
8815 #define SQLITE_TESTCTRL_SEEK_COUNT 30
8816 #define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */
8817
8818 /*
8819 ** CAPI3REF: SQL Keyword Checking
8820 **
8821 ** These routines provide access to the set of SQL language keywords
@@ -28182,16 +28183,19 @@
28183 EnableLookaside;
28184 }
28185 }
28186
28187 /*
28188 ** Take actions at the end of an API call to deal with error codes.
28189 */
28190 static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){
28191 if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
28192 sqlite3OomClear(db);
28193 sqlite3Error(db, SQLITE_NOMEM);
28194 return SQLITE_NOMEM_BKPT;
28195 }
28196 return rc & db->errMask;
28197 }
28198
28199 /*
28200 ** This function must be called before exiting any API function (i.e.
28201 ** returning control to the user) that has called sqlite3_malloc or
@@ -28209,12 +28213,12 @@
28213 ** Otherwise the read (and possible write) of db->mallocFailed
28214 ** is unsafe, as is the call to sqlite3Error().
28215 */
28216 assert( db!=0 );
28217 assert( sqlite3_mutex_held(db->mutex) );
28218 if( db->mallocFailed || rc ){
28219 return apiHandleError(db, rc);
28220 }
28221 return rc & db->errMask;
28222 }
28223
28224 /************** End of malloc.c **********************************************/
@@ -37003,11 +37007,28 @@
37007
37008 got = seekAndRead(pFile, offset, pBuf, amt);
37009 if( got==amt ){
37010 return SQLITE_OK;
37011 }else if( got<0 ){
37012 /* pFile->lastErrno has been set by seekAndRead().
37013 ** Usually we return SQLITE_IOERR_READ here, though for some
37014 ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The
37015 ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT
37016 ** prior to returning to the application by the sqlite3ApiExit()
37017 ** routine.
37018 */
37019 switch( pFile->lastErrno ){
37020 case ERANGE:
37021 case EIO:
37022 #ifdef ENXIO
37023 case ENXIO:
37024 #endif
37025 #ifdef EDEVERR
37026 case EDEVERR:
37027 #endif
37028 return SQLITE_IOERR_CORRUPTFS;
37029 }
37030 return SQLITE_IOERR_READ;
37031 }else{
37032 storeLastErrno(pFile, 0); /* not a system error */
37033 /* Unread parts of the buffer must be zero-filled */
37034 memset(&((char*)pBuf)[got], 0, amt-got);
@@ -38535,11 +38556,11 @@
38556 if( bUnlock ){
38557 rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
38558 if( rc==SQLITE_OK ){
38559 memset(&aLock[ofst], 0, sizeof(int)*n);
38560 }
38561 }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
38562 assert( n==1 && aLock[ofst]>1 );
38563 aLock[ofst]--;
38564 }
38565
38566 /* Undo the local locks */
@@ -38568,11 +38589,11 @@
38589 /* Make sure no sibling connections hold locks that will block this
38590 ** lock. If any do, return SQLITE_BUSY right away. */
38591 int ii;
38592 for(ii=ofst; ii<ofst+n; ii++){
38593 assert( (p->sharedMask & mask)==0 );
38594 if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
38595 rc = SQLITE_BUSY;
38596 break;
38597 }
38598 }
38599
@@ -39964,19 +39985,39 @@
39985 }
39986 return SQLITE_OK;
39987 }
39988
39989 /*
39990 ** If the last component of the pathname in z[0]..z[j-1] is something
39991 ** other than ".." then back it out and return true. If the last
39992 ** component is empty or if it is ".." then return false.
39993 */
39994 static int unixBackupDir(const char *z, int *pJ){
39995 int j = *pJ;
39996 int i;
39997 if( j<=0 ) return 0;
39998 for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){}
39999 if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
40000 *pJ = i-1;
40001 return 1;
40002 }
40003
40004 /*
40005 ** Convert a relative pathname into a full pathname. Also
40006 ** simplify the pathname as follows:
40007 **
40008 ** Remove all instances of /./
40009 ** Remove all isntances of /X/../ for any X
40010 */
40011 static int mkFullPathname(
40012 const char *zPath, /* Input path */
40013 char *zOut, /* Output buffer */
40014 int nOut /* Allocated size of buffer zOut */
40015 ){
40016 int nPath = sqlite3Strlen30(zPath);
40017 int iOff = 0;
40018 int i, j;
40019 if( zPath[0]!='/' ){
40020 if( osGetcwd(zOut, nOut-2)==0 ){
40021 return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
40022 }
40023 iOff = sqlite3Strlen30(zOut);
@@ -39987,10 +40028,45 @@
40028 ** even if it returns an error. */
40029 zOut[iOff] = '\0';
40030 return SQLITE_CANTOPEN_BKPT;
40031 }
40032 sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
40033
40034 /* Remove duplicate '/' characters. Except, two // at the beginning
40035 ** of a pathname is allowed since this is important on windows. */
40036 for(i=j=1; zOut[i]; i++){
40037 zOut[j++] = zOut[i];
40038 while( zOut[i]=='/' && zOut[i+1]=='/' ) i++;
40039 }
40040 zOut[j] = 0;
40041
40042 assert( zOut[0]=='/' );
40043 for(i=j=0; zOut[i]; i++){
40044 if( zOut[i]=='/' ){
40045 /* Skip over internal "/." directory components */
40046 if( zOut[i+1]=='.' && zOut[i+2]=='/' ){
40047 i += 1;
40048 continue;
40049 }
40050
40051 /* If this is a "/.." directory component then back out the
40052 ** previous term of the directory if it is something other than "..".
40053 */
40054 if( zOut[i+1]=='.'
40055 && zOut[i+2]=='.'
40056 && zOut[i+3]=='/'
40057 && unixBackupDir(zOut, &j)
40058 ){
40059 i += 2;
40060 continue;
40061 }
40062 }
40063 if( ALWAYS(j>=0) ) zOut[j] = zOut[i];
40064 j++;
40065 }
40066 if( NEVER(j==0) ) zOut[j++] = '/';
40067 zOut[j] = 0;
40068 return SQLITE_OK;
40069 }
40070
40071 /*
40072 ** Turn a relative pathname into a full pathname. The relative path
@@ -54326,10 +54402,11 @@
54402 sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */
54403 char *zSuperJournal = 0; /* Contents of super-journal file */
54404 i64 nSuperJournal; /* Size of super-journal file */
54405 char *zJournal; /* Pointer to one journal within MJ file */
54406 char *zSuperPtr; /* Space to hold super-journal filename */
54407 char *zFree = 0; /* Free this buffer */
54408 int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
54409
54410 /* Allocate space for both the pJournal and pSuper file descriptors.
54411 ** If successful, open the super-journal file for reading.
54412 */
@@ -54350,15 +54427,17 @@
54427 ** files extracted from regular rollback-journals.
54428 */
54429 rc = sqlite3OsFileSize(pSuper, &nSuperJournal);
54430 if( rc!=SQLITE_OK ) goto delsuper_out;
54431 nSuperPtr = pVfs->mxPathname+1;
54432 zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2);
54433 if( !zFree ){
54434 rc = SQLITE_NOMEM_BKPT;
54435 goto delsuper_out;
54436 }
54437 zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0;
54438 zSuperJournal = &zFree[4];
54439 zSuperPtr = &zSuperJournal[nSuperJournal+2];
54440 rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0);
54441 if( rc!=SQLITE_OK ) goto delsuper_out;
54442 zSuperJournal[nSuperJournal] = 0;
54443 zSuperJournal[nSuperJournal+1] = 0;
@@ -54402,11 +54481,11 @@
54481
54482 sqlite3OsClose(pSuper);
54483 rc = sqlite3OsDelete(pVfs, zSuper, 0);
54484
54485 delsuper_out:
54486 sqlite3_free(zFree);
54487 if( pSuper ){
54488 sqlite3OsClose(pSuper);
54489 assert( !isOpen(pJournal) );
54490 sqlite3_free(pSuper);
54491 }
@@ -54740,11 +54819,15 @@
54819 ** in case this has happened, clear the changeCountDone flag now.
54820 */
54821 pPager->changeCountDone = pPager->tempFile;
54822
54823 if( rc==SQLITE_OK ){
54824 /* Leave 4 bytes of space before the super-journal filename in memory.
54825 ** This is because it may end up being passed to sqlite3OsOpen(), in
54826 ** which case it requires 4 0x00 bytes in memory immediately before
54827 ** the filename. */
54828 zSuper = &pPager->pTmpSpace[4];
54829 rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
54830 testcase( rc!=SQLITE_OK );
54831 }
54832 if( rc==SQLITE_OK
54833 && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
@@ -54757,10 +54840,12 @@
54840 }
54841 if( rc==SQLITE_OK && zSuper[0] && res ){
54842 /* If there was a super-journal and this routine will return success,
54843 ** see if it is possible to delete the super-journal.
54844 */
54845 assert( zSuper==&pPager->pTmpSpace[4] );
54846 memset(&zSuper[-4], 0, 4);
54847 rc = pager_delsuper(pPager, zSuper);
54848 testcase( rc!=SQLITE_OK );
54849 }
54850 if( isHot && nPlayback ){
54851 sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s",
@@ -64746,11 +64831,11 @@
64831 #define hasReadConflicts(a, b) 0
64832 #endif
64833
64834 #ifdef SQLITE_DEBUG
64835 /*
64836 ** Return and reset the seek counter for a Btree object.
64837 */
64838 SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){
64839 u64 n = pBt->nSeek;
64840 pBt->nSeek = 0;
64841 return n;
@@ -82192,13 +82277,16 @@
82277 ** equal to, or greater than the second (double).
82278 */
82279 static int sqlite3IntFloatCompare(i64 i, double r){
82280 if( sizeof(LONGDOUBLE_TYPE)>8 ){
82281 LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
82282 testcase( x<r );
82283 testcase( x>r );
82284 testcase( x==r );
82285 if( x<r ) return -1;
82286 if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */
82287 return 0; /*NO_TEST*/ /* work around bugs in gcov */
82288 }else{
82289 i64 y;
82290 double s;
82291 if( r<-9223372036854775808.0 ) return +1;
82292 if( r>=9223372036854775808.0 ) return -1;
@@ -89388,11 +89476,11 @@
89476 assert( rc==SQLITE_OK );
89477 break;
89478 }
89479
89480
89481 /* Opcode: OpenEphemeral P1 P2 P3 P4 P5
89482 ** Synopsis: nColumn=P2
89483 **
89484 ** Open a new cursor P1 to a transient table.
89485 ** The cursor is always opened read/write even if
89486 ** the main database is read-only. The ephemeral
@@ -89408,10 +89496,14 @@
89496 **
89497 ** The P5 parameter can be a mask of the BTREE_* flags defined
89498 ** in btree.h. These flags control aspects of the operation of
89499 ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
89500 ** added automatically.
89501 **
89502 ** If P3 is positive, then reg[P3] is modified slightly so that it
89503 ** can be used as zero-length data for OP_Insert. This is an optimization
89504 ** that avoids an extra OP_Blob opcode to initialize that register.
89505 */
89506 /* Opcode: OpenAutoindex P1 P2 * P4 *
89507 ** Synopsis: nColumn=P2
89508 **
89509 ** This opcode works the same as OP_OpenEphemeral. It has a
@@ -89430,10 +89522,19 @@
89522 SQLITE_OPEN_EXCLUSIVE |
89523 SQLITE_OPEN_DELETEONCLOSE |
89524 SQLITE_OPEN_TRANSIENT_DB;
89525 assert( pOp->p1>=0 );
89526 assert( pOp->p2>=0 );
89527 if( pOp->p3>0 ){
89528 /* Make register reg[P3] into a value that can be used as the data
89529 ** form sqlite3BtreeInsert() where the length of the data is zero. */
89530 assert( pOp->p2==0 ); /* Only used when number of columns is zero */
89531 assert( pOp->opcode==OP_OpenEphemeral );
89532 assert( aMem[pOp->p3].flags & MEM_Null );
89533 aMem[pOp->p3].n = 0;
89534 aMem[pOp->p3].z = "";
89535 }
89536 pCx = p->apCsr[pOp->p1];
89537 if( pCx && pCx->pBtx ){
89538 /* If the ephermeral table is already open, erase all existing content
89539 ** so that the table is empty again, rather than creating a new table. */
89540 assert( pCx->isEphemeral );
@@ -90589,11 +90690,11 @@
90690 if( pOp->p5 & OPFLAG_ISNOOP ) break;
90691 #endif
90692
90693 if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
90694 if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
90695 assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 );
90696 x.pData = pData->z;
90697 x.nData = pData->n;
90698 seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
90699 if( pData->flags & MEM_Zero ){
90700 x.nZero = pData->u.nZero;
@@ -93644,11 +93745,15 @@
93745
93746 /* If we reach this point, it means that execution is finished with
93747 ** an error of some kind.
93748 */
93749 abort_due_to_error:
93750 if( db->mallocFailed ){
93751 rc = SQLITE_NOMEM_BKPT;
93752 }else if( rc==SQLITE_IOERR_CORRUPTFS ){
93753 rc = SQLITE_CORRUPT_BKPT;
93754 }
93755 assert( rc );
93756 if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
93757 sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
93758 }
93759 p->rc = rc;
@@ -99381,10 +99486,11 @@
99486 for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
99487 int iCol = -1;
99488 Expr *pE, *pDup;
99489 if( pItem->done ) continue;
99490 pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
99491 if( NEVER(pE==0) ) continue;
99492 if( sqlite3ExprIsInteger(pE, &iCol) ){
99493 if( iCol<=0 || iCol>pEList->nExpr ){
99494 resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
99495 return 1;
99496 }
@@ -99560,10 +99666,11 @@
99666 nResult = pSelect->pEList->nExpr;
99667 pParse = pNC->pParse;
99668 for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
99669 Expr *pE = pItem->pExpr;
99670 Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
99671 if( NEVER(pE2==0) ) continue;
99672 if( zType[0]!='G' ){
99673 iCol = resolveAsName(pParse, pSelect->pEList, pE2);
99674 if( iCol>0 ){
99675 /* If an AS-name match is found, mark this ORDER BY column as being
99676 ** a copy of the iCol-th result-set column. The subsequent call to
@@ -103679,10 +103786,11 @@
103786 ** register iReg. The caller must ensure that iReg already contains
103787 ** the correct value for the expression.
103788 */
103789 static void exprToRegister(Expr *pExpr, int iReg){
103790 Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
103791 if( NEVER(p==0) ) return;
103792 p->op2 = p->op;
103793 p->op = TK_REGISTER;
103794 p->iTable = iReg;
103795 ExprClearProperty(p, EP_Skip);
103796 }
@@ -104666,10 +104774,11 @@
104774 */
104775 SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
104776 int r2;
104777 pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
104778 if( ConstFactorOk(pParse)
104779 && ALWAYS(pExpr!=0)
104780 && pExpr->op!=TK_REGISTER
104781 && sqlite3ExprIsConstantNotJoin(pExpr)
104782 ){
104783 *pReg = 0;
104784 r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -119424,10 +119533,12 @@
119533 VFUNCTION(total_changes, 0, 0, 0, total_changes ),
119534 FUNCTION(replace, 3, 0, 0, replaceFunc ),
119535 FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
119536 FUNCTION(substr, 2, 0, 0, substrFunc ),
119537 FUNCTION(substr, 3, 0, 0, substrFunc ),
119538 FUNCTION(substring, 2, 0, 0, substrFunc ),
119539 FUNCTION(substring, 3, 0, 0, substrFunc ),
119540 WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
119541 WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
119542 WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
119543 WAGGREGATE(count, 0,0,0, countStep,
119544 countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
@@ -124310,10 +124421,12 @@
124421 /* Version 3.32.0 and later */
124422 char *(*create_filename)(const char*,const char*,const char*,
124423 int,const char**);
124424 void (*free_filename)(char*);
124425 sqlite3_file *(*database_file_object)(const char*);
124426 /* Version 3.34.0 and later */
124427 int (*txn_state)(sqlite3*,const char*);
124428 };
124429
124430 /*
124431 ** This is the function signature used for all extension entry points. It
124432 ** is also defined in the file "loadext.c".
@@ -124614,10 +124727,12 @@
124727 #define sqlite3_filename_wal sqlite3_api->filename_wal
124728 /* Version 3.32.0 and later */
124729 #define sqlite3_create_filename sqlite3_api->create_filename
124730 #define sqlite3_free_filename sqlite3_api->free_filename
124731 #define sqlite3_database_file_object sqlite3_api->database_file_object
124732 /* Version 3.34.0 and later */
124733 #define sqlite3_txn_state sqlite3_api->txn_state
124734 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
124735
124736 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
124737 /* This case when the file really is being compiled as a loadable
124738 ** extension */
@@ -125096,10 +125211,12 @@
125211 sqlite3_filename_wal,
125212 /* Version 3.32.0 and later */
125213 sqlite3_create_filename,
125214 sqlite3_free_filename,
125215 sqlite3_database_file_object,
125216 /* Version 3.34.0 and later */
125217 sqlite3_txn_state,
125218 };
125219
125220 /* True if x is the directory separator character
125221 */
125222 #if SQLITE_OS_WIN
@@ -131654,10 +131771,11 @@
131771 Column *aCol, *pCol; /* For looping over result columns */
131772 int nCol; /* Number of columns in the result set */
131773 char *zName; /* Column name */
131774 int nName; /* Size of name in zName[] */
131775 Hash ht; /* Hash table of column names */
131776 Table *pTab;
131777
131778 sqlite3HashInit(&ht);
131779 if( pEList ){
131780 nCol = pEList->nExpr;
131781 aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
@@ -131676,19 +131794,17 @@
131794 */
131795 if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){
131796 /* If the column contains an "AS <name>" phrase, use <name> as the name */
131797 }else{
131798 Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
131799 while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){
131800 pColExpr = pColExpr->pRight;
131801 assert( pColExpr!=0 );
131802 }
131803 if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){
131804 /* For columns use the column name name */
131805 int iCol = pColExpr->iColumn;
 
 
131806 if( iCol<0 ) iCol = pTab->iPKey;
131807 zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
131808 }else if( pColExpr->op==TK_ID ){
131809 assert( !ExprHasProperty(pColExpr, EP_IntValue) );
131810 zName = pColExpr->u.zToken;
@@ -136959,26 +137075,15 @@
137075 goto trigger_cleanup;
137076 }
137077 pTab = sqlite3SrcListLookup(pParse, pTableName);
137078 if( !pTab ){
137079 /* The table does not exist. */
137080 goto trigger_orphan_error;
 
 
 
 
 
 
 
 
 
 
 
137081 }
137082 if( IsVirtual(pTab) ){
137083 sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
137084 goto trigger_orphan_error;
137085 }
137086
137087 /* Check that the trigger name is not reserved and that no trigger of the
137088 ** specified name exists */
137089 zName = sqlite3NameFromToken(db, pName);
@@ -137012,16 +137117,16 @@
137117 ** of triggers.
137118 */
137119 if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
137120 sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
137121 (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
137122 goto trigger_orphan_error;
137123 }
137124 if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
137125 sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
137126 " trigger on table: %S", pTableName, 0);
137127 goto trigger_orphan_error;
137128 }
137129
137130 #ifndef SQLITE_OMIT_AUTHORIZATION
137131 if( !IN_RENAME_OBJECT ){
137132 int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -137077,10 +137182,27 @@
137182 if( !pParse->pNewTrigger ){
137183 sqlite3DeleteTrigger(db, pTrigger);
137184 }else{
137185 assert( pParse->pNewTrigger==pTrigger );
137186 }
137187 return;
137188
137189 trigger_orphan_error:
137190 if( db->init.iDb==1 ){
137191 /* Ticket #3810.
137192 ** Normally, whenever a table is dropped, all associated triggers are
137193 ** dropped too. But if a TEMP trigger is created on a non-TEMP table
137194 ** and the table is dropped by a different database connection, the
137195 ** trigger is not visible to the database connection that does the
137196 ** drop so the trigger cannot be dropped. This results in an
137197 ** "orphaned trigger" - a trigger whose associated table is missing.
137198 **
137199 ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df
137200 */
137201 db->init.orphanTrigger = 1;
137202 }
137203 goto trigger_cleanup;
137204 }
137205
137206 /*
137207 ** This routine is called after all of the trigger actions have been parsed
137208 ** in order to complete the process of building the trigger.
@@ -138664,10 +138786,12 @@
138786 sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
138787 }
138788
138789 if( nChangeFrom==0 && HasRowid(pTab) ){
138790 sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
138791 iEph = pParse->nTab++;
138792 addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet);
138793 }else{
138794 assert( pPk!=0 || HasRowid(pTab) );
138795 nPk = pPk ? pPk->nKeyCol : 0;
138796 iPk = pParse->nMem+1;
138797 pParse->nMem += nPk;
@@ -138755,13 +138879,14 @@
138879 /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
138880 ** mode, write the rowid into the FIFO. In either of the one-pass modes,
138881 ** leave it in register regOldRowid. */
138882 sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
138883 if( eOnePass==ONEPASS_OFF ){
 
138884 aRegIdx[nAllIdx] = ++pParse->nMem;
138885 sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid);
138886 }else{
138887 if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen);
138888 }
138889 }else{
138890 /* Read the PK of the current row into an array of registers. In
138891 ** ONEPASS_OFF mode, serialize the array into a record and store it in
138892 ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
@@ -138845,12 +138970,13 @@
138970 sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
138971 sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0);
138972 VdbeCoverage(v);
138973 }
138974 }else{
138975 sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
138976 labelContinue = sqlite3VdbeMakeLabel(pParse);
138977 addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid);
138978 VdbeCoverage(v);
138979 sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
138980 VdbeCoverage(v);
138981 }
138982 }
@@ -139096,15 +139222,13 @@
139222 if( eOnePass==ONEPASS_SINGLE ){
139223 /* Nothing to do at end-of-loop for a single-pass */
139224 }else if( eOnePass==ONEPASS_MULTI ){
139225 sqlite3VdbeResolveLabel(v, labelContinue);
139226 sqlite3WhereEnd(pWInfo);
139227 }else{
139228 sqlite3VdbeResolveLabel(v, labelContinue);
139229 sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
 
 
139230 }
139231 sqlite3VdbeResolveLabel(v, labelBreak);
139232
139233 /* Update the sqlite_sequence table by storing the content of the
139234 ** maximum rowid counter values recorded while inserting into
@@ -145894,10 +146018,11 @@
146018 ** all terms of the WHERE clause.
146019 */
146020 SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
146021 Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr);
146022 pWC->op = op;
146023 assert( pE2!=0 || pExpr==0 );
146024 if( pE2==0 ) return;
146025 if( pE2->op!=op ){
146026 whereClauseInsert(pWC, pExpr, 0);
146027 }else{
146028 sqlite3WhereSplit(pWC, pE2->pLeft, op);
@@ -146292,10 +146417,20 @@
146417 */
146418 static void createMask(WhereMaskSet *pMaskSet, int iCursor){
146419 assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
146420 pMaskSet->ix[pMaskSet->n++] = iCursor;
146421 }
146422
146423 /*
146424 ** If the right-hand branch of the expression is a TK_COLUMN, then return
146425 ** a pointer to the right-hand branch. Otherwise, return NULL.
146426 */
146427 static Expr *whereRightSubexprIsColumn(Expr *p){
146428 p = sqlite3ExprSkipCollateAndLikely(p->pRight);
146429 if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p;
146430 return 0;
146431 }
146432
146433 /*
146434 ** Advance to the next WhereTerm that matches according to the criteria
146435 ** established when the pScan object was initialized by whereScanInit().
146436 ** Return NULL if there are no more matching WhereTerms.
@@ -146323,12 +146458,11 @@
146458 pScan->pIdxExpr,iCur)==0)
146459 && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
146460 ){
146461 if( (pTerm->eOperator & WO_EQUIV)!=0
146462 && pScan->nEquiv<ArraySize(pScan->aiCur)
146463 && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0
 
146464 ){
146465 int j;
146466 for(j=0; j<pScan->nEquiv; j++){
146467 if( pScan->aiCur[j]==pX->iTable
146468 && pScan->aiColumn[j]==pX->iColumn ){
@@ -146520,11 +146654,12 @@
146654 int i;
146655 const char *zColl = pIdx->azColl[iCol];
146656
146657 for(i=0; i<pList->nExpr; i++){
146658 Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr);
146659 if( ALWAYS(p!=0)
146660 && p->op==TK_COLUMN
146661 && p->iColumn==pIdx->aiColumn[iCol]
146662 && p->iTable==iBase
146663 ){
146664 CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr);
146665 if( 0==sqlite3StrICmp(pColl->zName, zColl) ){
@@ -146584,10 +146719,11 @@
146719 ** true. Note: The (p->iTable==iBase) part of this test may be false if the
146720 ** current SELECT is a correlated sub-query.
146721 */
146722 for(i=0; i<pDistinct->nExpr; i++){
146723 Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr);
146724 if( NEVER(p==0) ) continue;
146725 if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
146726 }
146727
146728 /* Loop through all indices on the table, checking each to see if it makes
146729 ** the DISTINCT qualifier redundant. It does so if:
@@ -148498,13 +148634,13 @@
148634 LogEst rLogSize; /* Logarithm of table size */
148635 WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
148636
148637 pNew = pBuilder->pNew;
148638 if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
148639 WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n",
148640 pProbe->pTable->zName,pProbe->zName,
148641 pNew->u.btree.nEq, pNew->nSkip, pNew->rRun));
148642
148643 assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
148644 assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
148645 if( pNew->wsFlags & WHERE_BTM_LIMIT ){
148646 opMask = WO_LT|WO_LE;
@@ -148869,10 +149005,11 @@
149005
149006 if( pIndex->bUnordered ) return 0;
149007 if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
149008 for(ii=0; ii<pOB->nExpr; ii++){
149009 Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
149010 if( NEVER(pExpr==0) ) continue;
149011 if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
149012 if( pExpr->iColumn<0 ) return 1;
149013 for(jj=0; jj<pIndex->nKeyCol; jj++){
149014 if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
149015 }
@@ -149847,10 +149984,11 @@
149984 ** loops.
149985 */
149986 for(i=0; i<nOrderBy; i++){
149987 if( MASKBIT(i) & obSat ) continue;
149988 pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
149989 if( NEVER(pOBExpr==0) ) continue;
149990 if( pOBExpr->op!=TK_COLUMN ) continue;
149991 if( pOBExpr->iTable!=iCur ) continue;
149992 pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
149993 ~ready, eqOpMask, 0);
149994 if( pTerm==0 ) continue;
@@ -149973,10 +150111,11 @@
150111 for(i=0; bOnce && i<nOrderBy; i++){
150112 if( MASKBIT(i) & obSat ) continue;
150113 pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
150114 testcase( wctrlFlags & WHERE_GROUPBY );
150115 testcase( wctrlFlags & WHERE_DISTINCTBY );
150116 if( NEVER(pOBExpr==0) ) continue;
150117 if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
150118 if( iColumn>=XN_ROWID ){
150119 if( pOBExpr->op!=TK_COLUMN ) continue;
150120 if( pOBExpr->iTable!=iCur ) continue;
150121 if( pOBExpr->iColumn!=iColumn ) continue;
@@ -150136,11 +150275,11 @@
150275 rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
150276 rSortCost = nRow + rScale + 16;
150277
150278 /* Multiple by log(M) where M is the number of output rows.
150279 ** Use the LIMIT for M if it is smaller. Or if this sort is for
150280 ** a DISTINCT operator, M will be the number of distinct output
150281 ** rows, so fudge it downwards a bit.
150282 */
150283 if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
150284 nRow = pWInfo->iLimit;
150285 }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){
@@ -194220,11 +194359,11 @@
194359 p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
194360 p->nEvent = p->nSegment = 0;
194361 geopolyAddSegments(p, p1, 1);
194362 geopolyAddSegments(p, p2, 2);
194363 pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
194364 rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0;
194365 memset(aOverlap, 0, sizeof(aOverlap));
194366 while( pThisEvent ){
194367 if( pThisEvent->x!=rX ){
194368 GeoSegment *pPrev = 0;
194369 int iMask = 0;
@@ -212129,11 +212268,11 @@
212268 Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */
212269 ){
212270 int rc = SQLITE_OK; /* Return code */
212271 Fts5Bm25Data *p; /* Object to return */
212272
212273 p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0);
212274 if( p==0 ){
212275 int nPhrase; /* Number of phrases in query */
212276 sqlite3_int64 nRow = 0; /* Number of rows in table */
212277 sqlite3_int64 nToken = 0; /* Number of tokens in table */
212278 sqlite3_int64 nByte; /* Bytes of space to allocate */
@@ -212203,11 +212342,11 @@
212342 int nVal, /* Number of values in apVal[] array */
212343 sqlite3_value **apVal /* Array of trailing arguments */
212344 ){
212345 const double k1 = 1.2; /* Constant "k1" from BM25 formula */
212346 const double b = 0.75; /* Constant "b" from BM25 formula */
212347 int rc; /* Error code */
212348 double score = 0.0; /* SQL function return value */
212349 Fts5Bm25Data *pData; /* Values allocated/calculated once only */
212350 int i; /* Iterator variable */
212351 int nInst = 0; /* Value returned by xInstCount() */
212352 double D = 0.0; /* Total number of tokens in row */
@@ -212235,21 +212374,19 @@
212374 int nTok;
212375 rc = pApi->xColumnSize(pFts, -1, &nTok);
212376 D = (double)nTok;
212377 }
212378
212379 /* Determine and return the BM25 score for the current row. Or, if an
212380 ** error has occurred, throw an exception. */
 
 
 
 
 
 
 
 
212381 if( rc==SQLITE_OK ){
212382 for(i=0; i<pData->nPhrase; i++){
212383 score += pData->aIDF[i] * (
212384 ( aFreq[i] * (k1 + 1.0) ) /
212385 ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
212386 );
212387 }
212388 sqlite3_result_double(pCtx, -1.0 * score);
212389 }else{
212390 sqlite3_result_error_code(pCtx, rc);
212391 }
212392 }
@@ -223384,10 +223521,11 @@
223521 cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n);
223522 }
223523 }else{
223524 poslist.n = 0;
223525 fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
223526 fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0");
223527 while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
223528 int iCol = FTS5_POS2COLUMN(iPos);
223529 int iTokOff = FTS5_POS2OFFSET(iPos);
223530 cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n);
223531 }
@@ -226679,11 +226817,11 @@
226817 int nArg, /* Number of args */
226818 sqlite3_value **apUnused /* Function arguments */
226819 ){
226820 assert( nArg==0 );
226821 UNUSED_PARAM2(nArg, apUnused);
226822 sqlite3_result_text(pCtx, "fts5: 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -1, SQLITE_TRANSIENT);
226823 }
226824
226825 /*
226826 ** Return true if zName is the extension on one of the shadow tables used
226827 ** by this module.
@@ -229252,17 +229390,18 @@
229390
229391 /*
229392 ** Allocate a trigram tokenizer.
229393 */
229394 static int fts5TriCreate(
229395 void *pUnused,
229396 const char **azArg,
229397 int nArg,
229398 Fts5Tokenizer **ppOut
229399 ){
229400 int rc = SQLITE_OK;
229401 TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
229402 UNUSED_PARAM(pUnused);
229403 if( pNew==0 ){
229404 rc = SQLITE_NOMEM;
229405 }else{
229406 int i;
229407 pNew->bFold = 1;
@@ -229291,11 +229430,11 @@
229430 ** Trigram tokenizer tokenize routine.
229431 */
229432 static int fts5TriTokenize(
229433 Fts5Tokenizer *pTok,
229434 void *pCtx,
229435 int unusedFlags,
229436 const char *pText, int nText,
229437 int (*xToken)(void*, int, const char*, int, int, int)
229438 ){
229439 TrigramTokenizer *p = (TrigramTokenizer*)pTok;
229440 int rc = SQLITE_OK;
@@ -229302,10 +229441,11 @@
229441 char aBuf[32];
229442 const unsigned char *zIn = (const unsigned char*)pText;
229443 const unsigned char *zEof = &zIn[nText];
229444 u32 iCode;
229445
229446 UNUSED_PARAM(unusedFlags);
229447 while( 1 ){
229448 char *zOut = aBuf;
229449 int iStart = zIn - (const unsigned char*)pText;
229450 const unsigned char *zNext;
229451
@@ -230164,10 +230304,11 @@
230304 }
230305 iTbl++;
230306 }
230307 aAscii[0] = 0; /* 0x00 is never a token character */
230308 }
230309
230310
230311 /*
230312 ** 2015 May 30
230313 **
230314 ** The author disclaims copyright to this source code. In place of
@@ -231602,12 +231743,12 @@
231743 }
231744 #endif /* SQLITE_CORE */
231745 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
231746
231747 /************** End of stmt.c ************************************************/
231748 #if __LINE__!=231748
231749 #undef SQLITE_SOURCE_ID
231750 #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2"
231751 #endif
231752 /* Return the source-id for this library */
231753 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
231754 /************************** End of sqlite3.c ******************************/
231755
+5 -4
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -123,11 +123,11 @@
123123
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124124
** [sqlite_version()] and [sqlite_source_id()].
125125
*/
126126
#define SQLITE_VERSION "3.34.0"
127127
#define SQLITE_VERSION_NUMBER 3034000
128
-#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d"
128
+#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
129129
130130
/*
131131
** CAPI3REF: Run-Time Library Version Numbers
132132
** KEYWORDS: sqlite3_version sqlite3_sourceid
133133
**
@@ -502,10 +502,11 @@
502502
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
503503
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
504504
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
505505
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
506506
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
507
+#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
507508
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
508509
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
509510
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
510511
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
511512
#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -6190,11 +6191,11 @@
61906191
** CAPI3REF: Determine the transaction state of a database
61916192
** METHOD: sqlite3
61926193
**
61936194
** ^The sqlite3_txn_state(D,S) interface returns the current
61946195
** [transaction state] of schema S in database connection D. ^If S is NULL,
6195
-** then the highest transaction state of any schema on databse connection D
6196
+** then the highest transaction state of any schema on database connection D
61966197
** is returned. Transaction states are (in order of lowest to highest):
61976198
** <ol>
61986199
** <li value="0"> SQLITE_TXN_NONE
61996200
** <li value="1"> SQLITE_TXN_READ
62006201
** <li value="2"> SQLITE_TXN_WRITE
@@ -7737,11 +7738,10 @@
77377738
*/
77387739
#define SQLITE_TESTCTRL_FIRST 5
77397740
#define SQLITE_TESTCTRL_PRNG_SAVE 5
77407741
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
77417742
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
7742
-#define SQLITE_TESTCTRL_SEEK_COUNT 7
77437743
#define SQLITE_TESTCTRL_BITVEC_TEST 8
77447744
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
77457745
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
77467746
#define SQLITE_TESTCTRL_PENDING_BYTE 11
77477747
#define SQLITE_TESTCTRL_ASSERT 12
@@ -7762,11 +7762,12 @@
77627762
#define SQLITE_TESTCTRL_IMPOSTER 25
77637763
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
77647764
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
77657765
#define SQLITE_TESTCTRL_PRNG_SEED 28
77667766
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
7767
-#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
7767
+#define SQLITE_TESTCTRL_SEEK_COUNT 30
7768
+#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */
77687769
77697770
/*
77707771
** CAPI3REF: SQL Keyword Checking
77717772
**
77727773
** These routines provide access to the set of SQL language keywords
77737774
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -123,11 +123,11 @@
123 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124 ** [sqlite_version()] and [sqlite_source_id()].
125 */
126 #define SQLITE_VERSION "3.34.0"
127 #define SQLITE_VERSION_NUMBER 3034000
128 #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
@@ -502,10 +502,11 @@
502 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
503 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
504 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
505 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
506 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
 
507 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
508 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
509 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
510 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
511 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -6190,11 +6191,11 @@
6190 ** CAPI3REF: Determine the transaction state of a database
6191 ** METHOD: sqlite3
6192 **
6193 ** ^The sqlite3_txn_state(D,S) interface returns the current
6194 ** [transaction state] of schema S in database connection D. ^If S is NULL,
6195 ** then the highest transaction state of any schema on databse connection D
6196 ** is returned. Transaction states are (in order of lowest to highest):
6197 ** <ol>
6198 ** <li value="0"> SQLITE_TXN_NONE
6199 ** <li value="1"> SQLITE_TXN_READ
6200 ** <li value="2"> SQLITE_TXN_WRITE
@@ -7737,11 +7738,10 @@
7737 */
7738 #define SQLITE_TESTCTRL_FIRST 5
7739 #define SQLITE_TESTCTRL_PRNG_SAVE 5
7740 #define SQLITE_TESTCTRL_PRNG_RESTORE 6
7741 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
7742 #define SQLITE_TESTCTRL_SEEK_COUNT 7
7743 #define SQLITE_TESTCTRL_BITVEC_TEST 8
7744 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
7745 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
7746 #define SQLITE_TESTCTRL_PENDING_BYTE 11
7747 #define SQLITE_TESTCTRL_ASSERT 12
@@ -7762,11 +7762,12 @@
7762 #define SQLITE_TESTCTRL_IMPOSTER 25
7763 #define SQLITE_TESTCTRL_PARSER_COVERAGE 26
7764 #define SQLITE_TESTCTRL_RESULT_INTREAL 27
7765 #define SQLITE_TESTCTRL_PRNG_SEED 28
7766 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
7767 #define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
 
7768
7769 /*
7770 ** CAPI3REF: SQL Keyword Checking
7771 **
7772 ** These routines provide access to the set of SQL language keywords
7773
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -123,11 +123,11 @@
123 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124 ** [sqlite_version()] and [sqlite_source_id()].
125 */
126 #define SQLITE_VERSION "3.34.0"
127 #define SQLITE_VERSION_NUMBER 3034000
128 #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
@@ -502,10 +502,11 @@
502 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
503 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
504 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
505 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
506 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
507 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
508 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
509 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
510 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
511 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
512 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -6190,11 +6191,11 @@
6191 ** CAPI3REF: Determine the transaction state of a database
6192 ** METHOD: sqlite3
6193 **
6194 ** ^The sqlite3_txn_state(D,S) interface returns the current
6195 ** [transaction state] of schema S in database connection D. ^If S is NULL,
6196 ** then the highest transaction state of any schema on database connection D
6197 ** is returned. Transaction states are (in order of lowest to highest):
6198 ** <ol>
6199 ** <li value="0"> SQLITE_TXN_NONE
6200 ** <li value="1"> SQLITE_TXN_READ
6201 ** <li value="2"> SQLITE_TXN_WRITE
@@ -7737,11 +7738,10 @@
7738 */
7739 #define SQLITE_TESTCTRL_FIRST 5
7740 #define SQLITE_TESTCTRL_PRNG_SAVE 5
7741 #define SQLITE_TESTCTRL_PRNG_RESTORE 6
7742 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
 
7743 #define SQLITE_TESTCTRL_BITVEC_TEST 8
7744 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
7745 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
7746 #define SQLITE_TESTCTRL_PENDING_BYTE 11
7747 #define SQLITE_TESTCTRL_ASSERT 12
@@ -7762,11 +7762,12 @@
7762 #define SQLITE_TESTCTRL_IMPOSTER 25
7763 #define SQLITE_TESTCTRL_PARSER_COVERAGE 26
7764 #define SQLITE_TESTCTRL_RESULT_INTREAL 27
7765 #define SQLITE_TESTCTRL_PRNG_SEED 28
7766 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
7767 #define SQLITE_TESTCTRL_SEEK_COUNT 30
7768 #define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */
7769
7770 /*
7771 ** CAPI3REF: SQL Keyword Checking
7772 **
7773 ** These routines provide access to the set of SQL language keywords
7774
+23 -2
--- src/style.c
+++ src/style.c
@@ -1204,14 +1204,16 @@
12041204
@ <pre>
12051205
@ %h(blob_str(&g.httpHeader))
12061206
@ </pre>
12071207
}
12081208
}
1209
- style_finish_page("error");
1210
- if( zErr ){
1209
+ if( zErr && zErr[0] ){
1210
+ style_finish_page("error");
12111211
cgi_reply();
12121212
fossil_exit(1);
1213
+ }else{
1214
+ style_finish_page("test");
12131215
}
12141216
}
12151217
12161218
/*
12171219
** Generate a Not Yet Implemented error page.
@@ -1226,10 +1228,29 @@
12261228
void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){
12271229
fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr);
12281230
cgi_reset_content();
12291231
webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr);
12301232
}
1233
+
1234
+/*
1235
+** Issue a 404 Not Found error for a webpage
1236
+*/
1237
+void webpage_notfound_error(const char *zFormat, ...){
1238
+ char *zMsg;
1239
+ va_list ap;
1240
+ if( zFormat ){
1241
+ va_start(ap, zFormat);
1242
+ zMsg = vmprintf(zFormat, ap);
1243
+ va_end(ap);
1244
+ }else{
1245
+ zMsg = "Not Found";
1246
+ }
1247
+ style_header("Not Found");
1248
+ @ <p>%h(zMsg)</p>
1249
+ cgi_set_status(404, "Not Found");
1250
+ style_finish_page("enotfound");
1251
+}
12311252
12321253
#if INTERFACE
12331254
# define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);}
12341255
#endif
12351256
12361257
--- src/style.c
+++ src/style.c
@@ -1204,14 +1204,16 @@
1204 @ <pre>
1205 @ %h(blob_str(&g.httpHeader))
1206 @ </pre>
1207 }
1208 }
1209 style_finish_page("error");
1210 if( zErr ){
1211 cgi_reply();
1212 fossil_exit(1);
 
 
1213 }
1214 }
1215
1216 /*
1217 ** Generate a Not Yet Implemented error page.
@@ -1226,10 +1228,29 @@
1226 void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){
1227 fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr);
1228 cgi_reset_content();
1229 webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr);
1230 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1231
1232 #if INTERFACE
1233 # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);}
1234 #endif
1235
1236
--- src/style.c
+++ src/style.c
@@ -1204,14 +1204,16 @@
1204 @ <pre>
1205 @ %h(blob_str(&g.httpHeader))
1206 @ </pre>
1207 }
1208 }
1209 if( zErr && zErr[0] ){
1210 style_finish_page("error");
1211 cgi_reply();
1212 fossil_exit(1);
1213 }else{
1214 style_finish_page("test");
1215 }
1216 }
1217
1218 /*
1219 ** Generate a Not Yet Implemented error page.
@@ -1226,10 +1228,29 @@
1228 void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){
1229 fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr);
1230 cgi_reset_content();
1231 webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr);
1232 }
1233
1234 /*
1235 ** Issue a 404 Not Found error for a webpage
1236 */
1237 void webpage_notfound_error(const char *zFormat, ...){
1238 char *zMsg;
1239 va_list ap;
1240 if( zFormat ){
1241 va_start(ap, zFormat);
1242 zMsg = vmprintf(zFormat, ap);
1243 va_end(ap);
1244 }else{
1245 zMsg = "Not Found";
1246 }
1247 style_header("Not Found");
1248 @ <p>%h(zMsg)</p>
1249 cgi_set_status(404, "Not Found");
1250 style_finish_page("enotfound");
1251 }
1252
1253 #if INTERFACE
1254 # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);}
1255 #endif
1256
1257
+144 -30
--- src/timeline.c
+++ src/timeline.c
@@ -224,10 +224,26 @@
224224
*/
225225
int timeline_tableid(void){
226226
static int id = 0;
227227
return id++;
228228
}
229
+
230
+/*
231
+** Return true if the checking identified by "rid" has a valid "closed"
232
+** tag.
233
+*/
234
+static int has_closed_tag(int rid){
235
+ static Stmt q;
236
+ int res = 0;
237
+ db_static_prepare(&q,
238
+ "SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0",
239
+ TAG_CLOSED);
240
+ db_bind_int(&q, "$rid", rid);
241
+ res = db_step(&q)==SQLITE_ROW;
242
+ db_reset(&q);
243
+ return res;
244
+}
229245
230246
/*
231247
** Output a timeline in the web format given a query. The query
232248
** should return these columns:
233249
**
@@ -554,13 +570,11 @@
554570
}
555571
if( (tmFlags & TIMELINE_CLASSIC)!=0 ){
556572
if( zType[0]=='c' ){
557573
hyperlink_to_version(zUuid);
558574
if( isLeaf ){
559
- if( db_exists("SELECT 1 FROM tagxref"
560
- " WHERE rid=%d AND tagid=%d AND tagtype>0",
561
- rid, TAG_CLOSED) ){
575
+ if( has_closed_tag(rid) ){
562576
@ <span class="timelineLeaf">Closed-Leaf:</span>
563577
}else{
564578
@ <span class="timelineLeaf">Leaf:</span>
565579
}
566580
}
@@ -581,25 +595,32 @@
581595
if( zType[0]!='c' ){
582596
/* Comments for anything other than a check-in are generated by
583597
** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
584598
if( zType[0]=='w' ){
585599
const char *zCom = blob_str(&comment);
586
- char *zWiki;
600
+ /* Except, the comments generated by "fossil rebuild" for a wiki
601
+ ** page edit consist of a single character '-', '+', or ':' (to
602
+ ** indicate "deleted", "added", or "edited") followed by the
603
+ ** raw wiki page name. We have to generate an appropriate
604
+ ** comment on-the-fly
605
+ */
587606
wiki_hyperlink_override(zUuid);
588
- if( (tmFlags & TIMELINE_REFS)!=0
589
- && (zWiki = strstr(zCom,"wiki"))!=0
590
- ){
591
- /* The TIMELINE_REFS flag causes timeline comments of the
592
- ** form "Changes to wiki..." or "Added wiki" to be changed
593
- ** into just "Wiki..." */
594
- Blob rcom;
595
- blob_init(&rcom, 0, 0);
596
- blob_appendf(&rcom, "W%s", zWiki+1);
597
- wiki_convert(&rcom, 0, WIKI_INLINE);
598
- blob_reset(&rcom);
607
+ if( zCom[0]=='-' ){
608
+ @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\
609
+ @ %h(zCom+1)</a>
610
+ }else if( (tmFlags & TIMELINE_REFS)!=0
611
+ && (zCom[0]=='+' || zCom[0]==':') ){
612
+ @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>
613
+ }else if( zCom[0]=='+' ){
614
+ @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>
615
+ }else if( zCom[0]==':' ){
616
+ @ Changes to wiki page "%z(href("%R/wiki?name=%t",zCom+1))\
617
+ @ %h(zCom+1)</a>
599618
}else{
600
- wiki_convert(&comment, 0, WIKI_INLINE);
619
+ /* Legacy EVENT table entry that needs to be rebuilt */
620
+ @ Changes to a wiki page &rarr; Obsolete EVENT table information.
621
+ @ Run "fossil rebuild" on the repository.
601622
}
602623
wiki_hyperlink_override(0);
603624
}else{
604625
wiki_convert(&comment, 0, WIKI_INLINE);
605626
}
@@ -655,13 +676,11 @@
655676
}
656677
657678
if( (tmFlags & TIMELINE_CLASSIC)==0 ){
658679
if( zType[0]=='c' ){
659680
if( isLeaf ){
660
- if( db_exists("SELECT 1 FROM tagxref"
661
- " WHERE rid=%d AND tagid=%d AND tagtype>0",
662
- rid, TAG_CLOSED) ){
681
+ if( has_closed_tag(rid) ){
663682
@ <span class='timelineLeaf'>Closed-Leaf</span>
664683
}else{
665684
@ <span class='timelineLeaf'>Leaf</span>
666685
}
667686
}
@@ -1715,19 +1734,26 @@
17151734
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
17161735
int pd_rid;
17171736
double rBefore, rAfter, rCirca; /* Boundary times */
17181737
const char *z;
17191738
char *zOlderButton = 0; /* URL for Older button at the bottom */
1739
+ char *zOlderButtonLabel = 0; /* Label for the Older Button */
17201740
char *zNewerButton = 0; /* URL for Newer button at the top */
1741
+ char *zNewerButtonLabel = 0; /* Label for the Newer button */
17211742
int selectedRid = 0; /* Show a highlight on this RID */
17221743
int secondaryRid = 0; /* Show secondary highlight */
17231744
int disableY = 0; /* Disable type selector on submenu */
17241745
int advancedMenu = 0; /* Use the advanced menu design */
17251746
char *zPlural; /* Ending for plural forms */
17261747
int showCherrypicks = 1; /* True to show cherrypick merges */
1748
+ int haveParameterN; /* True if n= query parameter present */
1749
+
1750
+ url_initialize(&url, "timeline");
1751
+ cgi_query_parameters_to_url(&url);
17271752
17281753
/* Set number of rows to display */
1754
+ haveParameterN = P("n")!=0;
17291755
cookie_read_parameter("n","n");
17301756
z = P("n");
17311757
if( z==0 ) z = db_get("timeline-default-length",0);
17321758
if( z ){
17331759
if( fossil_strcmp(z,"all")==0 ){
@@ -1785,12 +1811,10 @@
17851811
}
17861812
if( zType[0]=='a' || zType[0]=='c' ){
17871813
cookie_write_parameter("y","y",zType);
17881814
}
17891815
cookie_render();
1790
- url_initialize(&url, "timeline");
1791
- cgi_query_parameters_to_url(&url);
17921816
17931817
/* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
17941818
if( P("cf")!=0 ){
17951819
zCirca = db_text(0,
17961820
"SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
@@ -2052,11 +2076,10 @@
20522076
" WHERE mlink.mid=x"
20532077
" AND mlink.fnid=filename.fnid AND %s)",
20542078
glob_expr("filename.name", zChng)
20552079
);
20562080
}
2057
-// tmFlags |= TIMELINE_DISJOINT;
20582081
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
20592082
db_multi_exec("%s", blob_sql_text(&sql));
20602083
if( advancedMenu ){
20612084
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
20622085
}
@@ -2106,10 +2129,11 @@
21062129
db_multi_exec("DELETE FROM ok");
21072130
}
21082131
if( p_rid ){
21092132
zBackTo = P("bt");
21102133
ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0;
2134
+ if( !haveParameterN ) nEntry = 0;
21112135
compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
21122136
np = db_int(0, "SELECT count(*)-1 FROM ok");
21132137
if( np>0 || nd==0 ){
21142138
if( nd>0 ) blob_appendf(&desc, " and ");
21152139
blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
@@ -2204,27 +2228,57 @@
22042228
}
22052229
if( bisectLocal || zBisect!=0 ){
22062230
blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
22072231
}
22082232
if( zYearMonth ){
2233
+ char *zNext;
22092234
zYearMonth = timeline_expand_datetime(zYearMonth);
2235
+ if( strlen(zYearMonth)>7 ){
2236
+ zYearMonth = mprintf("%.7s", zYearMonth);
2237
+ }
2238
+ if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
2239
+ zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
2240
+ }
2241
+ zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');",
2242
+ zYearMonth);
2243
+ if( db_int(0,
2244
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2245
+ " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)",
2246
+ zNext, blob_sql_text(&cond))
2247
+ ){
2248
+ zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2249
+ zNewerButtonLabel = "Following month";
2250
+ }
2251
+ fossil_free(zNext);
2252
+ zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');",
2253
+ zYearMonth);
2254
+ if( db_int(0,
2255
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2256
+ " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)",
2257
+ zYearMonth, blob_sql_text(&cond))
2258
+ ){
2259
+ zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2260
+ zOlderButtonLabel = "Previous month";
2261
+ }
2262
+ fossil_free(zNext);
22102263
blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
22112264
zYearMonth);
2265
+ nEntry = -1;
22122266
}
22132267
else if( zYearWeek ){
2214
- char *z;
2268
+ char *z, *zNext;
22152269
zYearWeek = timeline_expand_datetime(zYearWeek);
22162270
z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
22172271
if( z && z[0] ){
22182272
zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
22192273
zYearWeek);
22202274
zYearWeek = z;
22212275
}else{
22222276
if( strlen(zYearWeek)==7 ){
22232277
zYearWeekStart = db_text(0,
2224
- "SELECT date('%.4q-01-01','+%d days','weekday 1')",
2225
- zYearWeek, atoi(zYearWeek+5)*7);
2278
+ "SELECT date('%.4q-01-01','%+d days','weekday 1')",
2279
+ zYearWeek, atoi(zYearWeek+5)*7-6);
22262280
}else{
22272281
zYearWeekStart = 0;
22282282
}
22292283
if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){
22302284
zYearWeekStart = db_text(0,
@@ -2231,20 +2285,61 @@
22312285
"SELECT date('now','-6 days','weekday 1');");
22322286
zYearWeek = db_text(0,
22332287
"SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
22342288
}
22352289
}
2290
+ zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart);
2291
+ if( db_int(0,
2292
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2293
+ " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2294
+ zNext, blob_sql_text(&cond))
2295
+ ){
2296
+ zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2297
+ zNewerButtonLabel = "Following week";
2298
+ }
2299
+ fossil_free(zNext);
2300
+ zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart);
2301
+ if( db_int(0,
2302
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2303
+ " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2304
+ zYearWeekStart, blob_sql_text(&cond))
2305
+ ){
2306
+ zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2307
+ zOlderButtonLabel = "Previous week";
2308
+ }
2309
+ fossil_free(zNext);
22362310
blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
22372311
zYearWeek);
22382312
nEntry = -1;
22392313
}
22402314
else if( zDay ){
2315
+ char *zNext;
22412316
zDay = timeline_expand_datetime(zDay);
22422317
zDay = db_text(0, "SELECT date(%Q)", zDay);
22432318
if( zDay==0 || zDay[0]==0 ){
22442319
zDay = db_text(0, "SELECT date('now')");
22452320
}
2321
+ zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay);
2322
+ if( db_int(0,
2323
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2324
+ " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2325
+ zNext, blob_sql_text(&cond))
2326
+ ){
2327
+ zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2328
+ zNewerButtonLabel = "Following day";
2329
+ }
2330
+ fossil_free(zNext);
2331
+ zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay);
2332
+ if( db_int(0,
2333
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2334
+ " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2335
+ zDay, blob_sql_text(&cond))
2336
+ ){
2337
+ zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2338
+ zOlderButtonLabel = "Previous day";
2339
+ }
2340
+ fossil_free(zNext);
22462341
blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
22472342
zDay);
22482343
nEntry = -1;
22492344
}
22502345
else if( zNDays ){
@@ -2440,11 +2535,12 @@
24402535
db_multi_exec("%s", blob_sql_text(&sql));
24412536
24422537
n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
24432538
zPlural = n==1 ? "" : "s";
24442539
if( zYearMonth ){
2445
- blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth);
2540
+ blob_appendf(&desc, "%d %s%s for the month beginning %h-01",
2541
+ n, zEType, zPlural, zYearMonth);
24462542
}else if( zYearWeek ){
24472543
blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
24482544
n, zEType, zPlural, zYearWeek, zYearWeekStart);
24492545
}else if( zDay ){
24502546
blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay);
@@ -2532,10 +2628,11 @@
25322628
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
25332629
" WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
25342630
rDate-ONE_SECOND, blob_sql_text(&cond))
25352631
){
25362632
zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
2633
+ zOlderButtonLabel = "More";
25372634
}
25382635
free(zDate);
25392636
}
25402637
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
25412638
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
@@ -2547,10 +2644,11 @@
25472644
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
25482645
" WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
25492646
rDate+ONE_SECOND, blob_sql_text(&cond))
25502647
){
25512648
zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
2649
+ zNewerButtonLabel = "More";
25522650
}
25532651
free(zDate);
25542652
}
25552653
if( advancedMenu ){
25562654
if( zType[0]=='a' || zType[0]=='c' ){
@@ -2616,17 +2714,19 @@
26162714
if( zError ){
26172715
@ <p class="generalError">%h(zError)</p>
26182716
}
26192717
26202718
if( zNewerButton ){
2621
- @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
2719
+ @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
2720
+ @ &nbsp;&uarr;</a>
26222721
}
26232722
www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
26242723
selectedRid, secondaryRid, 0);
26252724
db_finalize(&q);
26262725
if( zOlderButton ){
2627
- @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
2726
+ @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
2727
+ @ &nbsp;&darr;</a>
26282728
}
26292729
document_emit_js(/*handles pikchrs rendered above*/);
26302730
style_finish_page("timeline");
26312731
}
26322732
@@ -2650,10 +2750,11 @@
26502750
** 3. Comment string and user
26512751
** 4. Number of non-merge children
26522752
** 5. Number of parents
26532753
** 6. mtime
26542754
** 7. branch
2755
+** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
26552756
*/
26562757
void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){
26572758
int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
26582759
int nLine = 0;
26592760
int nEntry = 0;
@@ -2674,10 +2775,11 @@
26742775
const char *zId = db_column_text(q, 1);
26752776
const char *zDate = db_column_text(q, 2);
26762777
const char *zCom = db_column_text(q, 3);
26772778
int nChild = db_column_int(q, 4);
26782779
int nParent = db_column_int(q, 5);
2780
+ const char *zType = db_column_text(q, 8);
26792781
char *zFree = 0;
26802782
int n = 0;
26812783
char zPrefix[80];
26822784
26832785
if( nAbsLimit!=0 ){
@@ -2717,11 +2819,22 @@
27172819
}
27182820
if( content_is_private(rid) ){
27192821
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* ");
27202822
n += strlen(zPrefix+n);
27212823
}
2722
- zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
2824
+ if( zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){
2825
+ /* Special processing for Wiki comments */
2826
+ if( zCom[0]=='+' ){
2827
+ zFree = mprintf("[%S] Add wiki page \"%s\"", zId, zCom+1);
2828
+ }else if( zCom[0]=='-' ){
2829
+ zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1);
2830
+ }else{
2831
+ zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
2832
+ }
2833
+ }else{
2834
+ zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
2835
+ }
27232836
/* record another X lines */
27242837
nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
27252838
fossil_free(zFree);
27262839
27272840
if(verboseFlag){
@@ -2787,11 +2900,12 @@
27872900
@ || ')' as comment,
27882901
@ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
27892902
@ AS primPlinkCount,
27902903
@ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
27912904
@ event.mtime AS mtime,
2792
- @ tagxref.value AS branch
2905
+ @ tagxref.value AS branch,
2906
+ @ event.type
27932907
@ FROM tag CROSS JOIN event CROSS JOIN blob
27942908
@ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
27952909
@ AND tagxref.tagtype>0
27962910
@ AND tagxref.rid=blob.rid
27972911
@ WHERE blob.rid=event.objid
27982912
--- src/timeline.c
+++ src/timeline.c
@@ -224,10 +224,26 @@
224 */
225 int timeline_tableid(void){
226 static int id = 0;
227 return id++;
228 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
230 /*
231 ** Output a timeline in the web format given a query. The query
232 ** should return these columns:
233 **
@@ -554,13 +570,11 @@
554 }
555 if( (tmFlags & TIMELINE_CLASSIC)!=0 ){
556 if( zType[0]=='c' ){
557 hyperlink_to_version(zUuid);
558 if( isLeaf ){
559 if( db_exists("SELECT 1 FROM tagxref"
560 " WHERE rid=%d AND tagid=%d AND tagtype>0",
561 rid, TAG_CLOSED) ){
562 @ <span class="timelineLeaf">Closed-Leaf:</span>
563 }else{
564 @ <span class="timelineLeaf">Leaf:</span>
565 }
566 }
@@ -581,25 +595,32 @@
581 if( zType[0]!='c' ){
582 /* Comments for anything other than a check-in are generated by
583 ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
584 if( zType[0]=='w' ){
585 const char *zCom = blob_str(&comment);
586 char *zWiki;
 
 
 
 
 
587 wiki_hyperlink_override(zUuid);
588 if( (tmFlags & TIMELINE_REFS)!=0
589 && (zWiki = strstr(zCom,"wiki"))!=0
590 ){
591 /* The TIMELINE_REFS flag causes timeline comments of the
592 ** form "Changes to wiki..." or "Added wiki" to be changed
593 ** into just "Wiki..." */
594 Blob rcom;
595 blob_init(&rcom, 0, 0);
596 blob_appendf(&rcom, "W%s", zWiki+1);
597 wiki_convert(&rcom, 0, WIKI_INLINE);
598 blob_reset(&rcom);
599 }else{
600 wiki_convert(&comment, 0, WIKI_INLINE);
 
 
601 }
602 wiki_hyperlink_override(0);
603 }else{
604 wiki_convert(&comment, 0, WIKI_INLINE);
605 }
@@ -655,13 +676,11 @@
655 }
656
657 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
658 if( zType[0]=='c' ){
659 if( isLeaf ){
660 if( db_exists("SELECT 1 FROM tagxref"
661 " WHERE rid=%d AND tagid=%d AND tagtype>0",
662 rid, TAG_CLOSED) ){
663 @ <span class='timelineLeaf'>Closed-Leaf</span>
664 }else{
665 @ <span class='timelineLeaf'>Leaf</span>
666 }
667 }
@@ -1715,19 +1734,26 @@
1715 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
1716 int pd_rid;
1717 double rBefore, rAfter, rCirca; /* Boundary times */
1718 const char *z;
1719 char *zOlderButton = 0; /* URL for Older button at the bottom */
 
1720 char *zNewerButton = 0; /* URL for Newer button at the top */
 
1721 int selectedRid = 0; /* Show a highlight on this RID */
1722 int secondaryRid = 0; /* Show secondary highlight */
1723 int disableY = 0; /* Disable type selector on submenu */
1724 int advancedMenu = 0; /* Use the advanced menu design */
1725 char *zPlural; /* Ending for plural forms */
1726 int showCherrypicks = 1; /* True to show cherrypick merges */
 
 
 
 
1727
1728 /* Set number of rows to display */
 
1729 cookie_read_parameter("n","n");
1730 z = P("n");
1731 if( z==0 ) z = db_get("timeline-default-length",0);
1732 if( z ){
1733 if( fossil_strcmp(z,"all")==0 ){
@@ -1785,12 +1811,10 @@
1785 }
1786 if( zType[0]=='a' || zType[0]=='c' ){
1787 cookie_write_parameter("y","y",zType);
1788 }
1789 cookie_render();
1790 url_initialize(&url, "timeline");
1791 cgi_query_parameters_to_url(&url);
1792
1793 /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
1794 if( P("cf")!=0 ){
1795 zCirca = db_text(0,
1796 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
@@ -2052,11 +2076,10 @@
2052 " WHERE mlink.mid=x"
2053 " AND mlink.fnid=filename.fnid AND %s)",
2054 glob_expr("filename.name", zChng)
2055 );
2056 }
2057 // tmFlags |= TIMELINE_DISJOINT;
2058 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2059 db_multi_exec("%s", blob_sql_text(&sql));
2060 if( advancedMenu ){
2061 style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
2062 }
@@ -2106,10 +2129,11 @@
2106 db_multi_exec("DELETE FROM ok");
2107 }
2108 if( p_rid ){
2109 zBackTo = P("bt");
2110 ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0;
 
2111 compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2112 np = db_int(0, "SELECT count(*)-1 FROM ok");
2113 if( np>0 || nd==0 ){
2114 if( nd>0 ) blob_appendf(&desc, " and ");
2115 blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
@@ -2204,27 +2228,57 @@
2204 }
2205 if( bisectLocal || zBisect!=0 ){
2206 blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
2207 }
2208 if( zYearMonth ){
 
2209 zYearMonth = timeline_expand_datetime(zYearMonth);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2210 blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
2211 zYearMonth);
 
2212 }
2213 else if( zYearWeek ){
2214 char *z;
2215 zYearWeek = timeline_expand_datetime(zYearWeek);
2216 z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
2217 if( z && z[0] ){
2218 zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
2219 zYearWeek);
2220 zYearWeek = z;
2221 }else{
2222 if( strlen(zYearWeek)==7 ){
2223 zYearWeekStart = db_text(0,
2224 "SELECT date('%.4q-01-01','+%d days','weekday 1')",
2225 zYearWeek, atoi(zYearWeek+5)*7);
2226 }else{
2227 zYearWeekStart = 0;
2228 }
2229 if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){
2230 zYearWeekStart = db_text(0,
@@ -2231,20 +2285,61 @@
2231 "SELECT date('now','-6 days','weekday 1');");
2232 zYearWeek = db_text(0,
2233 "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
2234 }
2235 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2236 blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
2237 zYearWeek);
2238 nEntry = -1;
2239 }
2240 else if( zDay ){
 
2241 zDay = timeline_expand_datetime(zDay);
2242 zDay = db_text(0, "SELECT date(%Q)", zDay);
2243 if( zDay==0 || zDay[0]==0 ){
2244 zDay = db_text(0, "SELECT date('now')");
2245 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2246 blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
2247 zDay);
2248 nEntry = -1;
2249 }
2250 else if( zNDays ){
@@ -2440,11 +2535,12 @@
2440 db_multi_exec("%s", blob_sql_text(&sql));
2441
2442 n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
2443 zPlural = n==1 ? "" : "s";
2444 if( zYearMonth ){
2445 blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth);
 
2446 }else if( zYearWeek ){
2447 blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
2448 n, zEType, zPlural, zYearWeek, zYearWeekStart);
2449 }else if( zDay ){
2450 blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay);
@@ -2532,10 +2628,11 @@
2532 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2533 " WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
2534 rDate-ONE_SECOND, blob_sql_text(&cond))
2535 ){
2536 zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
 
2537 }
2538 free(zDate);
2539 }
2540 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
2541 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
@@ -2547,10 +2644,11 @@
2547 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2548 " WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
2549 rDate+ONE_SECOND, blob_sql_text(&cond))
2550 ){
2551 zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
 
2552 }
2553 free(zDate);
2554 }
2555 if( advancedMenu ){
2556 if( zType[0]=='a' || zType[0]=='c' ){
@@ -2616,17 +2714,19 @@
2616 if( zError ){
2617 @ <p class="generalError">%h(zError)</p>
2618 }
2619
2620 if( zNewerButton ){
2621 @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
 
2622 }
2623 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
2624 selectedRid, secondaryRid, 0);
2625 db_finalize(&q);
2626 if( zOlderButton ){
2627 @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
 
2628 }
2629 document_emit_js(/*handles pikchrs rendered above*/);
2630 style_finish_page("timeline");
2631 }
2632
@@ -2650,10 +2750,11 @@
2650 ** 3. Comment string and user
2651 ** 4. Number of non-merge children
2652 ** 5. Number of parents
2653 ** 6. mtime
2654 ** 7. branch
 
2655 */
2656 void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){
2657 int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
2658 int nLine = 0;
2659 int nEntry = 0;
@@ -2674,10 +2775,11 @@
2674 const char *zId = db_column_text(q, 1);
2675 const char *zDate = db_column_text(q, 2);
2676 const char *zCom = db_column_text(q, 3);
2677 int nChild = db_column_int(q, 4);
2678 int nParent = db_column_int(q, 5);
 
2679 char *zFree = 0;
2680 int n = 0;
2681 char zPrefix[80];
2682
2683 if( nAbsLimit!=0 ){
@@ -2717,11 +2819,22 @@
2717 }
2718 if( content_is_private(rid) ){
2719 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* ");
2720 n += strlen(zPrefix+n);
2721 }
2722 zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
 
 
 
 
 
 
 
 
 
 
 
2723 /* record another X lines */
2724 nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
2725 fossil_free(zFree);
2726
2727 if(verboseFlag){
@@ -2787,11 +2900,12 @@
2787 @ || ')' as comment,
2788 @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
2789 @ AS primPlinkCount,
2790 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
2791 @ event.mtime AS mtime,
2792 @ tagxref.value AS branch
 
2793 @ FROM tag CROSS JOIN event CROSS JOIN blob
2794 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
2795 @ AND tagxref.tagtype>0
2796 @ AND tagxref.rid=blob.rid
2797 @ WHERE blob.rid=event.objid
2798
--- src/timeline.c
+++ src/timeline.c
@@ -224,10 +224,26 @@
224 */
225 int timeline_tableid(void){
226 static int id = 0;
227 return id++;
228 }
229
230 /*
231 ** Return true if the checking identified by "rid" has a valid "closed"
232 ** tag.
233 */
234 static int has_closed_tag(int rid){
235 static Stmt q;
236 int res = 0;
237 db_static_prepare(&q,
238 "SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0",
239 TAG_CLOSED);
240 db_bind_int(&q, "$rid", rid);
241 res = db_step(&q)==SQLITE_ROW;
242 db_reset(&q);
243 return res;
244 }
245
246 /*
247 ** Output a timeline in the web format given a query. The query
248 ** should return these columns:
249 **
@@ -554,13 +570,11 @@
570 }
571 if( (tmFlags & TIMELINE_CLASSIC)!=0 ){
572 if( zType[0]=='c' ){
573 hyperlink_to_version(zUuid);
574 if( isLeaf ){
575 if( has_closed_tag(rid) ){
 
 
576 @ <span class="timelineLeaf">Closed-Leaf:</span>
577 }else{
578 @ <span class="timelineLeaf">Leaf:</span>
579 }
580 }
@@ -581,25 +595,32 @@
595 if( zType[0]!='c' ){
596 /* Comments for anything other than a check-in are generated by
597 ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
598 if( zType[0]=='w' ){
599 const char *zCom = blob_str(&comment);
600 /* Except, the comments generated by "fossil rebuild" for a wiki
601 ** page edit consist of a single character '-', '+', or ':' (to
602 ** indicate "deleted", "added", or "edited") followed by the
603 ** raw wiki page name. We have to generate an appropriate
604 ** comment on-the-fly
605 */
606 wiki_hyperlink_override(zUuid);
607 if( zCom[0]=='-' ){
608 @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\
609 @ %h(zCom+1)</a>
610 }else if( (tmFlags & TIMELINE_REFS)!=0
611 && (zCom[0]=='+' || zCom[0]==':') ){
612 @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>
613 }else if( zCom[0]=='+' ){
614 @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>
615 }else if( zCom[0]==':' ){
616 @ Changes to wiki page "%z(href("%R/wiki?name=%t",zCom+1))\
617 @ %h(zCom+1)</a>
618 }else{
619 /* Legacy EVENT table entry that needs to be rebuilt */
620 @ Changes to a wiki page &rarr; Obsolete EVENT table information.
621 @ Run "fossil rebuild" on the repository.
622 }
623 wiki_hyperlink_override(0);
624 }else{
625 wiki_convert(&comment, 0, WIKI_INLINE);
626 }
@@ -655,13 +676,11 @@
676 }
677
678 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
679 if( zType[0]=='c' ){
680 if( isLeaf ){
681 if( has_closed_tag(rid) ){
 
 
682 @ <span class='timelineLeaf'>Closed-Leaf</span>
683 }else{
684 @ <span class='timelineLeaf'>Leaf</span>
685 }
686 }
@@ -1715,19 +1734,26 @@
1734 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
1735 int pd_rid;
1736 double rBefore, rAfter, rCirca; /* Boundary times */
1737 const char *z;
1738 char *zOlderButton = 0; /* URL for Older button at the bottom */
1739 char *zOlderButtonLabel = 0; /* Label for the Older Button */
1740 char *zNewerButton = 0; /* URL for Newer button at the top */
1741 char *zNewerButtonLabel = 0; /* Label for the Newer button */
1742 int selectedRid = 0; /* Show a highlight on this RID */
1743 int secondaryRid = 0; /* Show secondary highlight */
1744 int disableY = 0; /* Disable type selector on submenu */
1745 int advancedMenu = 0; /* Use the advanced menu design */
1746 char *zPlural; /* Ending for plural forms */
1747 int showCherrypicks = 1; /* True to show cherrypick merges */
1748 int haveParameterN; /* True if n= query parameter present */
1749
1750 url_initialize(&url, "timeline");
1751 cgi_query_parameters_to_url(&url);
1752
1753 /* Set number of rows to display */
1754 haveParameterN = P("n")!=0;
1755 cookie_read_parameter("n","n");
1756 z = P("n");
1757 if( z==0 ) z = db_get("timeline-default-length",0);
1758 if( z ){
1759 if( fossil_strcmp(z,"all")==0 ){
@@ -1785,12 +1811,10 @@
1811 }
1812 if( zType[0]=='a' || zType[0]=='c' ){
1813 cookie_write_parameter("y","y",zType);
1814 }
1815 cookie_render();
 
 
1816
1817 /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
1818 if( P("cf")!=0 ){
1819 zCirca = db_text(0,
1820 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
@@ -2052,11 +2076,10 @@
2076 " WHERE mlink.mid=x"
2077 " AND mlink.fnid=filename.fnid AND %s)",
2078 glob_expr("filename.name", zChng)
2079 );
2080 }
 
2081 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2082 db_multi_exec("%s", blob_sql_text(&sql));
2083 if( advancedMenu ){
2084 style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
2085 }
@@ -2106,10 +2129,11 @@
2129 db_multi_exec("DELETE FROM ok");
2130 }
2131 if( p_rid ){
2132 zBackTo = P("bt");
2133 ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0;
2134 if( !haveParameterN ) nEntry = 0;
2135 compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2136 np = db_int(0, "SELECT count(*)-1 FROM ok");
2137 if( np>0 || nd==0 ){
2138 if( nd>0 ) blob_appendf(&desc, " and ");
2139 blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
@@ -2204,27 +2228,57 @@
2228 }
2229 if( bisectLocal || zBisect!=0 ){
2230 blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
2231 }
2232 if( zYearMonth ){
2233 char *zNext;
2234 zYearMonth = timeline_expand_datetime(zYearMonth);
2235 if( strlen(zYearMonth)>7 ){
2236 zYearMonth = mprintf("%.7s", zYearMonth);
2237 }
2238 if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
2239 zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
2240 }
2241 zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');",
2242 zYearMonth);
2243 if( db_int(0,
2244 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2245 " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)",
2246 zNext, blob_sql_text(&cond))
2247 ){
2248 zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2249 zNewerButtonLabel = "Following month";
2250 }
2251 fossil_free(zNext);
2252 zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');",
2253 zYearMonth);
2254 if( db_int(0,
2255 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2256 " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)",
2257 zYearMonth, blob_sql_text(&cond))
2258 ){
2259 zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2260 zOlderButtonLabel = "Previous month";
2261 }
2262 fossil_free(zNext);
2263 blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
2264 zYearMonth);
2265 nEntry = -1;
2266 }
2267 else if( zYearWeek ){
2268 char *z, *zNext;
2269 zYearWeek = timeline_expand_datetime(zYearWeek);
2270 z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
2271 if( z && z[0] ){
2272 zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
2273 zYearWeek);
2274 zYearWeek = z;
2275 }else{
2276 if( strlen(zYearWeek)==7 ){
2277 zYearWeekStart = db_text(0,
2278 "SELECT date('%.4q-01-01','%+d days','weekday 1')",
2279 zYearWeek, atoi(zYearWeek+5)*7-6);
2280 }else{
2281 zYearWeekStart = 0;
2282 }
2283 if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){
2284 zYearWeekStart = db_text(0,
@@ -2231,20 +2285,61 @@
2285 "SELECT date('now','-6 days','weekday 1');");
2286 zYearWeek = db_text(0,
2287 "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
2288 }
2289 }
2290 zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart);
2291 if( db_int(0,
2292 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2293 " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2294 zNext, blob_sql_text(&cond))
2295 ){
2296 zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2297 zNewerButtonLabel = "Following week";
2298 }
2299 fossil_free(zNext);
2300 zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart);
2301 if( db_int(0,
2302 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2303 " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2304 zYearWeekStart, blob_sql_text(&cond))
2305 ){
2306 zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2307 zOlderButtonLabel = "Previous week";
2308 }
2309 fossil_free(zNext);
2310 blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
2311 zYearWeek);
2312 nEntry = -1;
2313 }
2314 else if( zDay ){
2315 char *zNext;
2316 zDay = timeline_expand_datetime(zDay);
2317 zDay = db_text(0, "SELECT date(%Q)", zDay);
2318 if( zDay==0 || zDay[0]==0 ){
2319 zDay = db_text(0, "SELECT date('now')");
2320 }
2321 zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay);
2322 if( db_int(0,
2323 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2324 " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2325 zNext, blob_sql_text(&cond))
2326 ){
2327 zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2328 zNewerButtonLabel = "Following day";
2329 }
2330 fossil_free(zNext);
2331 zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay);
2332 if( db_int(0,
2333 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2334 " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2335 zDay, blob_sql_text(&cond))
2336 ){
2337 zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2338 zOlderButtonLabel = "Previous day";
2339 }
2340 fossil_free(zNext);
2341 blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
2342 zDay);
2343 nEntry = -1;
2344 }
2345 else if( zNDays ){
@@ -2440,11 +2535,12 @@
2535 db_multi_exec("%s", blob_sql_text(&sql));
2536
2537 n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
2538 zPlural = n==1 ? "" : "s";
2539 if( zYearMonth ){
2540 blob_appendf(&desc, "%d %s%s for the month beginning %h-01",
2541 n, zEType, zPlural, zYearMonth);
2542 }else if( zYearWeek ){
2543 blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
2544 n, zEType, zPlural, zYearWeek, zYearWeekStart);
2545 }else if( zDay ){
2546 blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay);
@@ -2532,10 +2628,11 @@
2628 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2629 " WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
2630 rDate-ONE_SECOND, blob_sql_text(&cond))
2631 ){
2632 zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
2633 zOlderButtonLabel = "More";
2634 }
2635 free(zDate);
2636 }
2637 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
2638 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
@@ -2547,10 +2644,11 @@
2644 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2645 " WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
2646 rDate+ONE_SECOND, blob_sql_text(&cond))
2647 ){
2648 zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
2649 zNewerButtonLabel = "More";
2650 }
2651 free(zDate);
2652 }
2653 if( advancedMenu ){
2654 if( zType[0]=='a' || zType[0]=='c' ){
@@ -2616,17 +2714,19 @@
2714 if( zError ){
2715 @ <p class="generalError">%h(zError)</p>
2716 }
2717
2718 if( zNewerButton ){
2719 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
2720 @ &nbsp;&uarr;</a>
2721 }
2722 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
2723 selectedRid, secondaryRid, 0);
2724 db_finalize(&q);
2725 if( zOlderButton ){
2726 @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
2727 @ &nbsp;&darr;</a>
2728 }
2729 document_emit_js(/*handles pikchrs rendered above*/);
2730 style_finish_page("timeline");
2731 }
2732
@@ -2650,10 +2750,11 @@
2750 ** 3. Comment string and user
2751 ** 4. Number of non-merge children
2752 ** 5. Number of parents
2753 ** 6. mtime
2754 ** 7. branch
2755 ** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
2756 */
2757 void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){
2758 int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
2759 int nLine = 0;
2760 int nEntry = 0;
@@ -2674,10 +2775,11 @@
2775 const char *zId = db_column_text(q, 1);
2776 const char *zDate = db_column_text(q, 2);
2777 const char *zCom = db_column_text(q, 3);
2778 int nChild = db_column_int(q, 4);
2779 int nParent = db_column_int(q, 5);
2780 const char *zType = db_column_text(q, 8);
2781 char *zFree = 0;
2782 int n = 0;
2783 char zPrefix[80];
2784
2785 if( nAbsLimit!=0 ){
@@ -2717,11 +2819,22 @@
2819 }
2820 if( content_is_private(rid) ){
2821 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* ");
2822 n += strlen(zPrefix+n);
2823 }
2824 if( zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){
2825 /* Special processing for Wiki comments */
2826 if( zCom[0]=='+' ){
2827 zFree = mprintf("[%S] Add wiki page \"%s\"", zId, zCom+1);
2828 }else if( zCom[0]=='-' ){
2829 zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1);
2830 }else{
2831 zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
2832 }
2833 }else{
2834 zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
2835 }
2836 /* record another X lines */
2837 nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
2838 fossil_free(zFree);
2839
2840 if(verboseFlag){
@@ -2787,11 +2900,12 @@
2900 @ || ')' as comment,
2901 @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
2902 @ AS primPlinkCount,
2903 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
2904 @ event.mtime AS mtime,
2905 @ tagxref.value AS branch,
2906 @ event.type
2907 @ FROM tag CROSS JOIN event CROSS JOIN blob
2908 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
2909 @ AND tagxref.tagtype>0
2910 @ AND tagxref.rid=blob.rid
2911 @ WHERE blob.rid=event.objid
2912
+1
--- src/tkt.c
+++ src/tkt.c
@@ -446,10 +446,11 @@
446446
){
447447
goto ticket_schema_error;
448448
}
449449
break;
450450
}
451
+ case SQLITE_FUNCTION:
451452
case SQLITE_REINDEX:
452453
case SQLITE_TRANSACTION:
453454
case SQLITE_READ: {
454455
break;
455456
}
456457
--- src/tkt.c
+++ src/tkt.c
@@ -446,10 +446,11 @@
446 ){
447 goto ticket_schema_error;
448 }
449 break;
450 }
 
451 case SQLITE_REINDEX:
452 case SQLITE_TRANSACTION:
453 case SQLITE_READ: {
454 break;
455 }
456
--- src/tkt.c
+++ src/tkt.c
@@ -446,10 +446,11 @@
446 ){
447 goto ticket_schema_error;
448 }
449 break;
450 }
451 case SQLITE_FUNCTION:
452 case SQLITE_REINDEX:
453 case SQLITE_TRANSACTION:
454 case SQLITE_READ: {
455 break;
456 }
457
+1 -1
--- src/update.c
+++ src/update.c
@@ -595,11 +595,11 @@
595595
if( dryRunFlag ){
596596
db_end_transaction(1); /* With --dry-run, rollback changes */
597597
}else{
598598
char *zPwd;
599599
ensure_empty_dirs_created(1);
600
- sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0,
600
+ sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
601601
file_rmdir_sql_function, 0, 0);
602602
zPwd = file_getcwd(0,0);
603603
db_multi_exec(
604604
"SELECT rmdir(%Q||name) FROM dir_to_delete"
605605
" WHERE (%Q||name)<>%Q ORDER BY name DESC",
606606
--- src/update.c
+++ src/update.c
@@ -595,11 +595,11 @@
595 if( dryRunFlag ){
596 db_end_transaction(1); /* With --dry-run, rollback changes */
597 }else{
598 char *zPwd;
599 ensure_empty_dirs_created(1);
600 sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0,
601 file_rmdir_sql_function, 0, 0);
602 zPwd = file_getcwd(0,0);
603 db_multi_exec(
604 "SELECT rmdir(%Q||name) FROM dir_to_delete"
605 " WHERE (%Q||name)<>%Q ORDER BY name DESC",
606
--- src/update.c
+++ src/update.c
@@ -595,11 +595,11 @@
595 if( dryRunFlag ){
596 db_end_transaction(1); /* With --dry-run, rollback changes */
597 }else{
598 char *zPwd;
599 ensure_empty_dirs_created(1);
600 sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
601 file_rmdir_sql_function, 0, 0);
602 zPwd = file_getcwd(0,0);
603 db_multi_exec(
604 "SELECT rmdir(%Q||name) FROM dir_to_delete"
605 " WHERE (%Q||name)<>%Q ORDER BY name DESC",
606
+1 -1
--- src/wiki.c
+++ src/wiki.c
@@ -1299,11 +1299,11 @@
12991299
well_formed_wiki_name_rules();
13001300
CX("</div>"/*#wikiedit-tab-save*/);
13011301
}
13021302
builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
13031303
"storage", "popupwidget", "copybutton",
1304
- "pikchr", 0);
1304
+ "pikchr", NULL);
13051305
builtin_request_js("sbsdiff.js");
13061306
builtin_request_js("fossil.page.wikiedit.js");
13071307
builtin_fulfill_js_requests();
13081308
/* Dynamically populate the editor... */
13091309
style_script_begin(__FILE__,__LINE__);
13101310
--- src/wiki.c
+++ src/wiki.c
@@ -1299,11 +1299,11 @@
1299 well_formed_wiki_name_rules();
1300 CX("</div>"/*#wikiedit-tab-save*/);
1301 }
1302 builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
1303 "storage", "popupwidget", "copybutton",
1304 "pikchr", 0);
1305 builtin_request_js("sbsdiff.js");
1306 builtin_request_js("fossil.page.wikiedit.js");
1307 builtin_fulfill_js_requests();
1308 /* Dynamically populate the editor... */
1309 style_script_begin(__FILE__,__LINE__);
1310
--- src/wiki.c
+++ src/wiki.c
@@ -1299,11 +1299,11 @@
1299 well_formed_wiki_name_rules();
1300 CX("</div>"/*#wikiedit-tab-save*/);
1301 }
1302 builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
1303 "storage", "popupwidget", "copybutton",
1304 "pikchr", NULL);
1305 builtin_request_js("sbsdiff.js");
1306 builtin_request_js("fossil.page.wikiedit.js");
1307 builtin_fulfill_js_requests();
1308 /* Dynamically populate the editor... */
1309 style_script_begin(__FILE__,__LINE__);
1310
+1 -1
--- src/wiki.c
+++ src/wiki.c
@@ -1299,11 +1299,11 @@
12991299
well_formed_wiki_name_rules();
13001300
CX("</div>"/*#wikiedit-tab-save*/);
13011301
}
13021302
builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
13031303
"storage", "popupwidget", "copybutton",
1304
- "pikchr", 0);
1304
+ "pikchr", NULL);
13051305
builtin_request_js("sbsdiff.js");
13061306
builtin_request_js("fossil.page.wikiedit.js");
13071307
builtin_fulfill_js_requests();
13081308
/* Dynamically populate the editor... */
13091309
style_script_begin(__FILE__,__LINE__);
13101310
--- src/wiki.c
+++ src/wiki.c
@@ -1299,11 +1299,11 @@
1299 well_formed_wiki_name_rules();
1300 CX("</div>"/*#wikiedit-tab-save*/);
1301 }
1302 builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
1303 "storage", "popupwidget", "copybutton",
1304 "pikchr", 0);
1305 builtin_request_js("sbsdiff.js");
1306 builtin_request_js("fossil.page.wikiedit.js");
1307 builtin_fulfill_js_requests();
1308 /* Dynamically populate the editor... */
1309 style_script_begin(__FILE__,__LINE__);
1310
--- src/wiki.c
+++ src/wiki.c
@@ -1299,11 +1299,11 @@
1299 well_formed_wiki_name_rules();
1300 CX("</div>"/*#wikiedit-tab-save*/);
1301 }
1302 builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer",
1303 "storage", "popupwidget", "copybutton",
1304 "pikchr", NULL);
1305 builtin_request_js("sbsdiff.js");
1306 builtin_request_js("fossil.page.wikiedit.js");
1307 builtin_fulfill_js_requests();
1308 /* Dynamically populate the editor... */
1309 style_script_begin(__FILE__,__LINE__);
1310
+1 -12
--- www/alerts.md
+++ www/alerts.md
@@ -530,22 +530,11 @@
530530
531531
532532
<a id="backup"></a>
533533
## Cloning, Syncing, and Backups
534534
535
-The Admin → Notification settings are not replicated using clone or
536
-sync, and it is not possible to push such settings from one repository
537
-to another. In a network of peer repositories, you only want one
538
-repository sending email alerts. If you were to replicate the email
539
-alert settings to a separate repository, then subscribers would get
540
-multiple alerts for each event, which would be bad.
541
-
542
-However, the subscriber list can be synced for backup purposes. Use the
543
-[`fossil config pull subscriber`](/help?cmd=configuration) command to
544
-pull the latest subscriber list from a server into a backup repository.
545
-
546
-The `push`, `export`, and `import` commands all work similarly.
535
+That’s [covered elsewhere](./backup.md#alerts).
547536
548537
549538
<a id="pages" name="commands"></a>
550539
## Controlling the Email Alert System
551540
552541
--- www/alerts.md
+++ www/alerts.md
@@ -530,22 +530,11 @@
530
531
532 <a id="backup"></a>
533 ## Cloning, Syncing, and Backups
534
535 The Admin → Notification settings are not replicated using clone or
536 sync, and it is not possible to push such settings from one repository
537 to another. In a network of peer repositories, you only want one
538 repository sending email alerts. If you were to replicate the email
539 alert settings to a separate repository, then subscribers would get
540 multiple alerts for each event, which would be bad.
541
542 However, the subscriber list can be synced for backup purposes. Use the
543 [`fossil config pull subscriber`](/help?cmd=configuration) command to
544 pull the latest subscriber list from a server into a backup repository.
545
546 The `push`, `export`, and `import` commands all work similarly.
547
548
549 <a id="pages" name="commands"></a>
550 ## Controlling the Email Alert System
551
552
--- www/alerts.md
+++ www/alerts.md
@@ -530,22 +530,11 @@
530
531
532 <a id="backup"></a>
533 ## Cloning, Syncing, and Backups
534
535 That’s [covered elsewhere](./backup.md#alerts).
 
 
 
 
 
 
 
 
 
 
 
536
537
538 <a id="pages" name="commands"></a>
539 ## Controlling the Email Alert System
540
541
+48 -11
--- www/backup.md
+++ www/backup.md
@@ -26,17 +26,54 @@
2626
reasons.
2727
2828
2929
## <a id="config"></a> Configuration Drift
3030
31
-Fossil allows the local configuration in certain areas to differ from
32
-that of the remote. With the exception of the prior item, you get a copy
33
-of these configuration areas on initial clone, but after that, some
34
-remote configuration changes don’t sync down automatically, such as the
35
-remote’s skin. You can ask for updates by running the
36
-[`fossil config pull skin`](./help?cmd=config) command, but that
37
-does not happen automatically during the course of normal development.
31
+Fossil allows the local configuration to differ in several areas from
32
+that of the remote. You get a copy
33
+of *some* of these configuration areas on initial clone — not all! — but after that,
34
+remote configuration changes mostly do not sync down automatically.
35
+
36
+
37
+#### <a id="skin"></a> Skin
38
+
39
+Changes to the remote’s skin don’t sync down, on purpose, since you may
40
+want to have a different skin on the local clone than on the remote. You
41
+can ask for updates with [`fossil config pull skin`][cfg], but that does
42
+not happen automatically during the course of normal development.
43
+
44
+
45
+#### <a id="alerts"></a> Email Alerts
46
+
47
+The Admin → Notification settings do not get copied on clone or sync,
48
+and it is not possible to push such settings from one repository to
49
+another. We did this on purpose because you may have a network of peer
50
+repositories, and you only want one repository sending email alerts. If
51
+Fossil were to automatically replicate the email alert settings to a
52
+separate repository, subscribers would get multiple alerts for each
53
+event, which would be *bad.*
54
+
55
+The only element of the email alert configuration that can be pulled
56
+over the sync protocol on demand is the subscriber list, via
57
+[`fossil config pull subscriber`][cfg].
58
+
59
+
60
+#### <a id="project"></a> Project Configuration
61
+
62
+This is normally generated once during `fossil init` and never changed,
63
+so Fossil doesn’t pull this information without being forced, on
64
+purpose. You could accidentally merge two separate Fossil repos by
65
+pushing one repo’s project config up to another, for example.
66
+
67
+
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
+
3875
3976
4077
## <a id="private"></a> Private Branches
4178
4279
The very nature of Fossil’s [private branch feature][pbr] ensures that
@@ -92,11 +129,11 @@
92129
source of truth, those users still syncing with `svr2` won’t have their
93130
commits pushed up to `svr1` unless you’ve set up bidirectional sync,
94131
rather than have the two backup servers do `pull` only.
95132
96133
97
-# Solution 1: Explicit Pulls
134
+# <a id="sync-solution"></a> Solution 1: Explicit Pulls
98135
99136
The following script solves most of the above problems for the use case
100137
where you want a *nearly-complete* clone of the remote repository using nothing
101138
but the normal Fossil sync protocol. It only does so if you are logged into
102139
the remote as a user with Setup capability, however.
@@ -119,11 +156,11 @@
119156
that it will continue to have information that the remote says should
120157
not exist any more. That would be not so much a “backup” as an
121158
“archive,” which might not be what you want.
122159
123160
124
-# Solution 2: SQL-Level Backup
161
+# <a id="sql-solution"></a> Solution 2: SQL-Level Backup
125162
126163
The first method doesn’t get you a copy of the remote’s
127164
[private branches][pbr], on purpose. It may also miss other info on the
128165
remote, such as SQL-level customizations that the sync protocol can’t
129166
see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can
@@ -211,11 +248,11 @@
211248
care of. If all variables defined in earlier scripts are available, then
212249
restoration is:
213250
214251
```
215252
openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" |
216
- xz -d | fossil --no-repository ~/museum/restored-repo.fossil
253
+ xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil
217254
```
218255
219256
We changed the `-e` to `-d` on the `openssl` command to get decryption,
220257
and we changed the `-out` to `-in` so it reads from the encrypted backup
221258
file and writes the result to stdout.
@@ -228,11 +265,11 @@
228265
restoration:
229266
Fossil serves as a dogfooding project for SQLite,
230267
often making use of the latest features, so it is quite likely that a given
231268
random `sqlite3` binary in your `PATH` will be unable to understand the
232269
file created by “`fossil sql .dump`”! The tricky bit is, you can’t just
233
-pipe the decrpted SQL dump into `fossil sql`, because on startup, Fossil
270
+pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil
234271
normally goes looking for tables created by `fossil init`, and it won’t
235272
find them in a newly-created repo DB. We get around this by passing
236273
the `--no-repository` flag, which suppresses this behavior. Doing it
237274
this way saves you from needing to go and build a matching version of
238275
`sqlite3` just to restore the backup.
239276
--- www/backup.md
+++ www/backup.md
@@ -26,17 +26,54 @@
26 reasons.
27
28
29 ## <a id="config"></a> Configuration Drift
30
31 Fossil allows the local configuration in certain areas to differ from
32 that of the remote. With the exception of the prior item, you get a copy
33 of these configuration areas on initial clone, but after that, some
34 remote configuration changes don’t sync down automatically, such as the
35 remote’s skin. You can ask for updates by running the
36 [`fossil config pull skin`](./help?cmd=config) command, but that
37 does not happen automatically during the course of normal development.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
39
40 ## <a id="private"></a> Private Branches
41
42 The very nature of Fossil’s [private branch feature][pbr] ensures that
@@ -92,11 +129,11 @@
92 source of truth, those users still syncing with `svr2` won’t have their
93 commits pushed up to `svr1` unless you’ve set up bidirectional sync,
94 rather than have the two backup servers do `pull` only.
95
96
97 # Solution 1: Explicit Pulls
98
99 The following script solves most of the above problems for the use case
100 where you want a *nearly-complete* clone of the remote repository using nothing
101 but the normal Fossil sync protocol. It only does so if you are logged into
102 the remote as a user with Setup capability, however.
@@ -119,11 +156,11 @@
119 that it will continue to have information that the remote says should
120 not exist any more. That would be not so much a “backup” as an
121 “archive,” which might not be what you want.
122
123
124 # Solution 2: SQL-Level Backup
125
126 The first method doesn’t get you a copy of the remote’s
127 [private branches][pbr], on purpose. It may also miss other info on the
128 remote, such as SQL-level customizations that the sync protocol can’t
129 see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can
@@ -211,11 +248,11 @@
211 care of. If all variables defined in earlier scripts are available, then
212 restoration is:
213
214 ```
215 openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" |
216 xz -d | fossil --no-repository ~/museum/restored-repo.fossil
217 ```
218
219 We changed the `-e` to `-d` on the `openssl` command to get decryption,
220 and we changed the `-out` to `-in` so it reads from the encrypted backup
221 file and writes the result to stdout.
@@ -228,11 +265,11 @@
228 restoration:
229 Fossil serves as a dogfooding project for SQLite,
230 often making use of the latest features, so it is quite likely that a given
231 random `sqlite3` binary in your `PATH` will be unable to understand the
232 file created by “`fossil sql .dump`”! The tricky bit is, you can’t just
233 pipe the decrpted SQL dump into `fossil sql`, because on startup, Fossil
234 normally goes looking for tables created by `fossil init`, and it won’t
235 find them in a newly-created repo DB. We get around this by passing
236 the `--no-repository` flag, which suppresses this behavior. Doing it
237 this way saves you from needing to go and build a matching version of
238 `sqlite3` just to restore the backup.
239
--- www/backup.md
+++ www/backup.md
@@ -26,17 +26,54 @@
26 reasons.
27
28
29 ## <a id="config"></a> Configuration Drift
30
31 Fossil allows the local configuration to differ in several areas from
32 that of the remote. You get a copy
33 of *some* of these configuration areas on initial clone — not all! — but after that,
34 remote configuration changes mostly do not sync down automatically.
35
36
37 #### <a id="skin"></a> Skin
38
39 Changes to the remote’s skin don’t sync down, on purpose, since you may
40 want to have a different skin on the local clone than on the remote. You
41 can ask for updates with [`fossil config pull skin`][cfg], but that does
42 not happen automatically during the course of normal development.
43
44
45 #### <a id="alerts"></a> Email Alerts
46
47 The Admin → Notification settings do not get copied on clone or sync,
48 and it is not possible to push such settings from one repository to
49 another. We did this on purpose because you may have a network of peer
50 repositories, and you only want one repository sending email alerts. If
51 Fossil were to automatically replicate the email alert settings to a
52 separate repository, subscribers would get multiple alerts for each
53 event, which would be *bad.*
54
55 The only element of the email alert configuration that can be pulled
56 over the sync protocol on demand is the subscriber list, via
57 [`fossil config pull subscriber`][cfg].
58
59
60 #### <a id="project"></a> Project Configuration
61
62 This is normally generated once during `fossil init` and never changed,
63 so Fossil doesn’t pull this information without being forced, on
64 purpose. You could accidentally merge two separate Fossil repos by
65 pushing one repo’s project config up to another, for example.
66
67
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
79 The very nature of Fossil’s [private branch feature][pbr] ensures that
@@ -92,11 +129,11 @@
129 source of truth, those users still syncing with `svr2` won’t have their
130 commits pushed up to `svr1` unless you’ve set up bidirectional sync,
131 rather than have the two backup servers do `pull` only.
132
133
134 # <a id="sync-solution"></a> Solution 1: Explicit Pulls
135
136 The following script solves most of the above problems for the use case
137 where you want a *nearly-complete* clone of the remote repository using nothing
138 but the normal Fossil sync protocol. It only does so if you are logged into
139 the remote as a user with Setup capability, however.
@@ -119,11 +156,11 @@
156 that it will continue to have information that the remote says should
157 not exist any more. That would be not so much a “backup” as an
158 “archive,” which might not be what you want.
159
160
161 # <a id="sql-solution"></a> Solution 2: SQL-Level Backup
162
163 The first method doesn’t get you a copy of the remote’s
164 [private branches][pbr], on purpose. It may also miss other info on the
165 remote, such as SQL-level customizations that the sync protocol can’t
166 see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can
@@ -211,11 +248,11 @@
248 care of. If all variables defined in earlier scripts are available, then
249 restoration is:
250
251 ```
252 openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" |
253 xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil
254 ```
255
256 We changed the `-e` to `-d` on the `openssl` command to get decryption,
257 and we changed the `-out` to `-in` so it reads from the encrypted backup
258 file and writes the result to stdout.
@@ -228,11 +265,11 @@
265 restoration:
266 Fossil serves as a dogfooding project for SQLite,
267 often making use of the latest features, so it is quite likely that a given
268 random `sqlite3` binary in your `PATH` will be unable to understand the
269 file created by “`fossil sql .dump`”! The tricky bit is, you can’t just
270 pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil
271 normally goes looking for tables created by `fossil init`, and it won’t
272 find them in a newly-created repo DB. We get around this by passing
273 the `--no-repository` flag, which suppresses this behavior. Doing it
274 this way saves you from needing to go and build a matching version of
275 `sqlite3` just to restore the backup.
276
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -56,11 +56,11 @@
5656
pull the documentation file from the local source tree on disk, not
5757
from the any check-in. The "<b>ckout</b>" keyword
5858
only works when you start your server using the
5959
"[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]"
6060
commands. The "/doc/ckout" URL is intended to show a preview of
61
-the documentation you are currently editing but have not yet you checked in.
61
+the documentation you are currently editing but have not yet checked in.
6262
6363
The original designed purpose of the "ckout" feature is to allow the
6464
user to preview local changes to documentation before committing the
6565
change. This is an important facility, since unlike other document
6666
languages like HTML, there is still a lot of variation among rendering
6767
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -56,11 +56,11 @@
56 pull the documentation file from the local source tree on disk, not
57 from the any check-in. The "<b>ckout</b>" keyword
58 only works when you start your server using the
59 "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]"
60 commands. The "/doc/ckout" URL is intended to show a preview of
61 the documentation you are currently editing but have not yet you checked in.
62
63 The original designed purpose of the "ckout" feature is to allow the
64 user to preview local changes to documentation before committing the
65 change. This is an important facility, since unlike other document
66 languages like HTML, there is still a lot of variation among rendering
67
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -56,11 +56,11 @@
56 pull the documentation file from the local source tree on disk, not
57 from the any check-in. The "<b>ckout</b>" keyword
58 only works when you start your server using the
59 "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]"
60 commands. The "/doc/ckout" URL is intended to show a preview of
61 the documentation you are currently editing but have not yet checked in.
62
63 The original designed purpose of the "ckout" feature is to allow the
64 user to preview local changes to documentation before committing the
65 change. This is an important facility, since unlike other document
66 languages like HTML, there is still a lot of variation among rendering
67
--- www/fileedit-page.md
+++ www/fileedit-page.md
@@ -9,11 +9,11 @@
99
1010
Predictably, the ability to edit files in a repository from a web
1111
browser halfway around the world comes with several obligatory caveats
1212
and disclaimers...
1313
14
-## `/fileedit` Does *Nothing* by Default.
14
+## <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
1818
[fileedit-glob](/help?cmd=fileedit-glob) repository setting to a
1919
comma- or newline-delimited list of globs representing a whitelist of
@@ -20,11 +20,11 @@
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
25
-## CSRF & HTTP Referrer Headers
25
+## <a id="csrf"></a> CSRF & HTTP Referrer Headers
2626
2727
In order to protect against [Cross-site Request Forgery (CSRF)][csrf]
2828
attacks, Fossil UI features which write to the database require that
2929
the browser send the so-called [HTTP `Referer` header][referer]
3030
(noting that the misspelling of "referrer" is a historical accident
@@ -53,16 +53,16 @@
5353
5454
[referer]: https://en.wikipedia.org/wiki/HTTP_referer
5555
[csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery
5656
[xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest
5757
58
-## `/fileedit` **Works by Creating Commits**
58
+## <a id="commit"></a> `/fileedit` **Works by Creating Commits**
5959
6060
Thus any edits made via that page become a normal part of the
6161
repository.
6262
63
-## `/fileedit` is *Intended* for use with Embedded Docs
63
+## <a id="intent"></a> `/fileedit` is *Intended* for use with Embedded Docs
6464
6565
... and similar text files, and is most certainly
6666
**not intended for editing code**.
6767
6868
Editing files with unusual syntax requirements, e.g. hard tabs in
@@ -75,11 +75,11 @@
7575
changes. **Files with mixed EOL styles** *will be normalized to a single
7676
EOL style* when modified using `/fileedit`. When "inheriting" the EOL
7777
style from a previous version which has mixed styles, the first EOL
7878
style detected in the previous version of the file is used.
7979
80
-## `/fileedit` **is Not a Replacement for a Checkout**
80
+## <a id="checkout"></a> `/fileedit` **is Not a Replacement for a Checkout**
8181
8282
A full-featured checkout allows far more possibilities than this basic
8383
online editor permits, and the feature scope of `/fileedit` is
8484
intentionally kept small, implementing only the bare necessities
8585
needed for performing basic edits online. It *is not, and will never
@@ -108,11 +108,11 @@
108108
whether or not to implement them subject to notable contributor
109109
debate. e.g. the ability to add new files or remove/rename older
110110
files.
111111
112112
113
-## `/fileedit` **Stores Only Limited Local Edits While Working**
113
+## <a id="storage"></a> `/fileedit` **Stores Only Limited Local Edits While Working**
114114
115115
When changes are made to a given checkin/file combination,
116116
`/fileedit` will, if possible, store them in [`window.localStorage`
117117
or `window.sessionStorage`][html5storage], if available, but...
118118
@@ -142,23 +142,23 @@
142142
If `/filepage` determines that no peristent storage is available a
143143
warning is displayed on the editor page.
144144
145145
[html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
146146
147
-## The Power is Yours, but...
147
+## <a id="power"></a> The Power is Yours, but...
148148
149149
> "With great power comes great responsibility."
150150
151151
**Use this feature judiciously, *if at all*.**
152152
153153
Now, with those warnings and caveats out of the way...
154154
155155
-----
156156
157
-# Tips and Tricks
157
+# <a id="tips"></a> Tips and Tricks
158158
159
-## `fossil` Global-scope JS Object
159
+## <a id="global-js"></a> `fossil` Global-scope JS Object
160160
161161
`/fileedit` is largely implemented in JavaScript, and makes heavy use
162162
of the global-scope `fossil` object, which provides
163163
infrastructure-level features intended for use by Fossil UI pages.
164164
(That said, that infrastructure was introduced with `/fileedit`, and
@@ -169,11 +169,11 @@
169169
listening to page-specific events so that JS code installed via
170170
[client-side edits to the site skin's footer](customskin.md) may react
171171
to those changes somehow. The next section describes one such use for
172172
such events...
173173
174
-## Integrating Syntax Highlighting
174
+## <a id="syn-hl"></a> Integrating Syntax Highlighting
175175
176176
Assuming a repository has integrated a 3rd-party syntax highlighting
177177
solution, it can probably (depending on its API) be told how to
178178
highlight `/fileedit`'s wiki/markdown-format previews. Here are
179179
instructions for doing so with [highlightjs](https://highlightjs.org/):
@@ -223,11 +223,11 @@
223223
The event listener callback shown above doesn't use the `mimetype`,
224224
but makes used of the other two. It fishes all `code` blocks out of
225225
the preview which explicitly have a CSS class named
226226
`language-`something, and then asks highlightjs to highlight them.
227227
228
-## Integrating a Custom Editor Widget
228
+## <a id="editor"></a> Integrating a Custom Editor Widget
229229
230230
(These instructions also work for the `/wikiedit` page by eplacing
231231
"fileedit" with "wikiedit" in any strings or symbol names!)
232232
233233
It is possible to replace `/filepage`'s basic text-editing widget (a
234234
--- www/fileedit-page.md
+++ www/fileedit-page.md
@@ -9,11 +9,11 @@
9
10 Predictably, the ability to edit files in a repository from a web
11 browser halfway around the world comes with several obligatory caveats
12 and disclaimers...
13
14 ## `/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,11 +20,11 @@
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
25 ## CSRF & HTTP Referrer Headers
26
27 In order to protect against [Cross-site Request Forgery (CSRF)][csrf]
28 attacks, Fossil UI features which write to the database require that
29 the browser send the so-called [HTTP `Referer` header][referer]
30 (noting that the misspelling of "referrer" is a historical accident
@@ -53,16 +53,16 @@
53
54 [referer]: https://en.wikipedia.org/wiki/HTTP_referer
55 [csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery
56 [xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest
57
58 ## `/fileedit` **Works by Creating Commits**
59
60 Thus any edits made via that page become a normal part of the
61 repository.
62
63 ## `/fileedit` is *Intended* for use with Embedded Docs
64
65 ... and similar text files, and is most certainly
66 **not intended for editing code**.
67
68 Editing files with unusual syntax requirements, e.g. hard tabs in
@@ -75,11 +75,11 @@
75 changes. **Files with mixed EOL styles** *will be normalized to a single
76 EOL style* when modified using `/fileedit`. When "inheriting" the EOL
77 style from a previous version which has mixed styles, the first EOL
78 style detected in the previous version of the file is used.
79
80 ## `/fileedit` **is Not a Replacement for a Checkout**
81
82 A full-featured checkout allows far more possibilities than this basic
83 online editor permits, and the feature scope of `/fileedit` is
84 intentionally kept small, implementing only the bare necessities
85 needed for performing basic edits online. It *is not, and will never
@@ -108,11 +108,11 @@
108 whether or not to implement them subject to notable contributor
109 debate. e.g. the ability to add new files or remove/rename older
110 files.
111
112
113 ## `/fileedit` **Stores Only Limited Local Edits While Working**
114
115 When changes are made to a given checkin/file combination,
116 `/fileedit` will, if possible, store them in [`window.localStorage`
117 or `window.sessionStorage`][html5storage], if available, but...
118
@@ -142,23 +142,23 @@
142 If `/filepage` determines that no peristent storage is available a
143 warning is displayed on the editor page.
144
145 [html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
146
147 ## The Power is Yours, but...
148
149 > "With great power comes great responsibility."
150
151 **Use this feature judiciously, *if at all*.**
152
153 Now, with those warnings and caveats out of the way...
154
155 -----
156
157 # Tips and Tricks
158
159 ## `fossil` Global-scope JS Object
160
161 `/fileedit` is largely implemented in JavaScript, and makes heavy use
162 of the global-scope `fossil` object, which provides
163 infrastructure-level features intended for use by Fossil UI pages.
164 (That said, that infrastructure was introduced with `/fileedit`, and
@@ -169,11 +169,11 @@
169 listening to page-specific events so that JS code installed via
170 [client-side edits to the site skin's footer](customskin.md) may react
171 to those changes somehow. The next section describes one such use for
172 such events...
173
174 ## Integrating Syntax Highlighting
175
176 Assuming a repository has integrated a 3rd-party syntax highlighting
177 solution, it can probably (depending on its API) be told how to
178 highlight `/fileedit`'s wiki/markdown-format previews. Here are
179 instructions for doing so with [highlightjs](https://highlightjs.org/):
@@ -223,11 +223,11 @@
223 The event listener callback shown above doesn't use the `mimetype`,
224 but makes used of the other two. It fishes all `code` blocks out of
225 the preview which explicitly have a CSS class named
226 `language-`something, and then asks highlightjs to highlight them.
227
228 ## Integrating a Custom Editor Widget
229
230 (These instructions also work for the `/wikiedit` page by eplacing
231 "fileedit" with "wikiedit" in any strings or symbol names!)
232
233 It is possible to replace `/filepage`'s basic text-editing widget (a
234
--- www/fileedit-page.md
+++ www/fileedit-page.md
@@ -9,11 +9,11 @@
9
10 Predictably, the ability to edit files in a repository from a web
11 browser halfway around the world comes with several obligatory caveats
12 and disclaimers...
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,11 +20,11 @@
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
25 ## <a id="csrf"></a> CSRF & HTTP Referrer Headers
26
27 In order to protect against [Cross-site Request Forgery (CSRF)][csrf]
28 attacks, Fossil UI features which write to the database require that
29 the browser send the so-called [HTTP `Referer` header][referer]
30 (noting that the misspelling of "referrer" is a historical accident
@@ -53,16 +53,16 @@
53
54 [referer]: https://en.wikipedia.org/wiki/HTTP_referer
55 [csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery
56 [xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest
57
58 ## <a id="commit"></a> `/fileedit` **Works by Creating Commits**
59
60 Thus any edits made via that page become a normal part of the
61 repository.
62
63 ## <a id="intent"></a> `/fileedit` is *Intended* for use with Embedded Docs
64
65 ... and similar text files, and is most certainly
66 **not intended for editing code**.
67
68 Editing files with unusual syntax requirements, e.g. hard tabs in
@@ -75,11 +75,11 @@
75 changes. **Files with mixed EOL styles** *will be normalized to a single
76 EOL style* when modified using `/fileedit`. When "inheriting" the EOL
77 style from a previous version which has mixed styles, the first EOL
78 style detected in the previous version of the file is used.
79
80 ## <a id="checkout"></a> `/fileedit` **is Not a Replacement for a Checkout**
81
82 A full-featured checkout allows far more possibilities than this basic
83 online editor permits, and the feature scope of `/fileedit` is
84 intentionally kept small, implementing only the bare necessities
85 needed for performing basic edits online. It *is not, and will never
@@ -108,11 +108,11 @@
108 whether or not to implement them subject to notable contributor
109 debate. e.g. the ability to add new files or remove/rename older
110 files.
111
112
113 ## <a id="storage"></a> `/fileedit` **Stores Only Limited Local Edits While Working**
114
115 When changes are made to a given checkin/file combination,
116 `/fileedit` will, if possible, store them in [`window.localStorage`
117 or `window.sessionStorage`][html5storage], if available, but...
118
@@ -142,23 +142,23 @@
142 If `/filepage` determines that no peristent storage is available a
143 warning is displayed on the editor page.
144
145 [html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
146
147 ## <a id="power"></a> The Power is Yours, but...
148
149 > "With great power comes great responsibility."
150
151 **Use this feature judiciously, *if at all*.**
152
153 Now, with those warnings and caveats out of the way...
154
155 -----
156
157 # <a id="tips"></a> Tips and Tricks
158
159 ## <a id="global-js"></a> `fossil` Global-scope JS Object
160
161 `/fileedit` is largely implemented in JavaScript, and makes heavy use
162 of the global-scope `fossil` object, which provides
163 infrastructure-level features intended for use by Fossil UI pages.
164 (That said, that infrastructure was introduced with `/fileedit`, and
@@ -169,11 +169,11 @@
169 listening to page-specific events so that JS code installed via
170 [client-side edits to the site skin's footer](customskin.md) may react
171 to those changes somehow. The next section describes one such use for
172 such events...
173
174 ## <a id="syn-hl"></a> Integrating Syntax Highlighting
175
176 Assuming a repository has integrated a 3rd-party syntax highlighting
177 solution, it can probably (depending on its API) be told how to
178 highlight `/fileedit`'s wiki/markdown-format previews. Here are
179 instructions for doing so with [highlightjs](https://highlightjs.org/):
@@ -223,11 +223,11 @@
223 The event listener callback shown above doesn't use the `mimetype`,
224 but makes used of the other two. It fishes all `code` blocks out of
225 the preview which explicitly have a CSS class named
226 `language-`something, and then asks highlightjs to highlight them.
227
228 ## <a id="editor"></a> Integrating a Custom Editor Widget
229
230 (These instructions also work for the `/wikiedit` page by eplacing
231 "fileedit" with "wikiedit" in any strings or symbol names!)
232
233 It is possible to replace `/filepage`'s basic text-editing widget (a
234
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -936,11 +936,11 @@
936936
isn't normally synchronized with a "<tt>fossil clone</tt>" command unless
937937
you add the "-u" option. (See "[./aboutdownload.wiki|How the
938938
Download Page Works]" for details.) There may also be some purely
939939
static elements of the web site served via D. Richard Hipp's own
940940
lightweight web server,
941
- <tt>[https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md|althttpd]</tt>,
941
+ <tt>[https://sqlite.org/althttpd/|althttpd]</tt>,
942942
which is configured as a front end to Fossil running in CGI mode on
943943
these sites.
944944
945945
<li><p>That estimate is based on pricing at Digital Ocean in
946946
mid-2019: Fossil will run just fine on the smallest instance they
947947
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -936,11 +936,11 @@
936 isn't normally synchronized with a "<tt>fossil clone</tt>" command unless
937 you add the "-u" option. (See "[./aboutdownload.wiki|How the
938 Download Page Works]" for details.) There may also be some purely
939 static elements of the web site served via D. Richard Hipp's own
940 lightweight web server,
941 <tt>[https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md|althttpd]</tt>,
942 which is configured as a front end to Fossil running in CGI mode on
943 these sites.
944
945 <li><p>That estimate is based on pricing at Digital Ocean in
946 mid-2019: Fossil will run just fine on the smallest instance they
947
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -936,11 +936,11 @@
936 isn't normally synchronized with a "<tt>fossil clone</tt>" command unless
937 you add the "-u" option. (See "[./aboutdownload.wiki|How the
938 Download Page Works]" for details.) There may also be some purely
939 static elements of the web site served via D. Richard Hipp's own
940 lightweight web server,
941 <tt>[https://sqlite.org/althttpd/|althttpd]</tt>,
942 which is configured as a front end to Fossil running in CGI mode on
943 these sites.
944
945 <li><p>That estimate is based on pricing at Digital Ocean in
946 mid-2019: Fossil will run just fine on the smallest instance they
947
--- www/hashpolicy.wiki
+++ www/hashpolicy.wiki
@@ -51,15 +51,15 @@
5151
The Hardened SHA1 algorithm automatically detects when the artifact
5252
being hashed is specifically designed to exploit the known weaknesses
5353
in the SHA1 algorithm, and when it detects such an attack it changes
5454
the hash algorithm (by increasing the number of rounds in the compression
5555
function) to make the algorithm secure again. If the attack detection
56
-gets a false possible, that means that Hardened SHA1 will get a different
56
+gets a false-positive, that means that Hardened SHA1 will get a different
5757
answer than the standard FIPS PUB 180-4 SHA1, but the creators of
5858
Hardened SHA1 (see the second paper
5959
&#91;[https://marc-stevens.nl/research/papers/C13-S.pdf|2]&#93;)
60
-report that the probability of a false positive is vanishingly small -
60
+report that the probability of a false-positive is vanishingly small -
6161
less than 1 false positive out of 10<sup><font size=1>27</font></sup>
6262
hashes.
6363
6464
Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that
6565
much hashing, so performance is not really an issue.
6666
--- www/hashpolicy.wiki
+++ www/hashpolicy.wiki
@@ -51,15 +51,15 @@
51 The Hardened SHA1 algorithm automatically detects when the artifact
52 being hashed is specifically designed to exploit the known weaknesses
53 in the SHA1 algorithm, and when it detects such an attack it changes
54 the hash algorithm (by increasing the number of rounds in the compression
55 function) to make the algorithm secure again. If the attack detection
56 gets a false possible, that means that Hardened SHA1 will get a different
57 answer than the standard FIPS PUB 180-4 SHA1, but the creators of
58 Hardened SHA1 (see the second paper
59 &#91;[https://marc-stevens.nl/research/papers/C13-S.pdf|2]&#93;)
60 report that the probability of a false positive is vanishingly small -
61 less than 1 false positive out of 10<sup><font size=1>27</font></sup>
62 hashes.
63
64 Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that
65 much hashing, so performance is not really an issue.
66
--- www/hashpolicy.wiki
+++ www/hashpolicy.wiki
@@ -51,15 +51,15 @@
51 The Hardened SHA1 algorithm automatically detects when the artifact
52 being hashed is specifically designed to exploit the known weaknesses
53 in the SHA1 algorithm, and when it detects such an attack it changes
54 the hash algorithm (by increasing the number of rounds in the compression
55 function) to make the algorithm secure again. If the attack detection
56 gets a false-positive, that means that Hardened SHA1 will get a different
57 answer than the standard FIPS PUB 180-4 SHA1, but the creators of
58 Hardened SHA1 (see the second paper
59 &#91;[https://marc-stevens.nl/research/papers/C13-S.pdf|2]&#93;)
60 report that the probability of a false-positive is vanishingly small -
61 less than 1 false positive out of 10<sup><font size=1>27</font></sup>
62 hashes.
63
64 Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that
65 much hashing, so performance is not really an issue.
66
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -148,10 +148,11 @@
148148
<li> <a href='history.md'>Purpose and History of Fossil</a>
149149
<li> <a href='build.wiki'>Compiling and installing Fossil</a>
150150
<li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
151151
<li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
152152
<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
153
+<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
153154
<li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
154155
book</a>
155156
</ul>
156157
<a name="pindex"></a>
157158
<h2>Permuted Index:</h2>
158159
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -148,10 +148,11 @@
148 <li> <a href='history.md'>Purpose and History of Fossil</a>
149 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
150 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
151 <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
152 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
 
153 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
154 book</a>
155 </ul>
156 <a name="pindex"></a>
157 <h2>Permuted Index:</h2>
158
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -148,10 +148,11 @@
148 <li> <a href='history.md'>Purpose and History of Fossil</a>
149 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
150 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
151 <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
152 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
153 <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
154 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
155 book</a>
156 </ul>
157 <a name="pindex"></a>
158 <h2>Permuted Index:</h2>
159
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -13,10 +13,11 @@
1313
<li> <a href='history.md'>Purpose and History of Fossil</a>
1414
<li> <a href='build.wiki'>Compiling and installing Fossil</a>
1515
<li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
1616
<li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
1717
<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
18
+<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
1819
<li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
1920
book</a>
2021
</ul>
2122
<a name="pindex"></a>
2223
<h2>Permuted Index:</h2>
2324
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -13,10 +13,11 @@
13 <li> <a href='history.md'>Purpose and History of Fossil</a>
14 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
15 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
16 <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
17 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
 
18 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
19 book</a>
20 </ul>
21 <a name="pindex"></a>
22 <h2>Permuted Index:</h2>
23
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -13,10 +13,11 @@
13 <li> <a href='history.md'>Purpose and History of Fossil</a>
14 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
15 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
16 <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
17 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
18 <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
19 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
20 book</a>
21 </ul>
22 <a name="pindex"></a>
23 <h2>Permuted Index:</h2>
24
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -36,7 +36,7 @@
3636
you created to start using your Fossil server.
3737
3838
*[Return to the top-level Fossil server article.](../)*
3939
4040
41
-[althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md
41
+[althttpd]: https://sqlite.org/althttpd/
4242
[cgi]: ../../cgi.wiki
4343
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -36,7 +36,7 @@
36 you created to start using your Fossil server.
37
38 *[Return to the top-level Fossil server article.](../)*
39
40
41 [althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md
42 [cgi]: ../../cgi.wiki
43
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -36,7 +36,7 @@
36 you created to start using your Fossil server.
37
38 *[Return to the top-level Fossil server article.](../)*
39
40
41 [althttpd]: https://sqlite.org/althttpd/
42 [cgi]: ../../cgi.wiki
43
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -217,25 +217,46 @@
217217
The most common thing people get wrong when hand-rolling a configuration
218218
like this is to get the slashes wrong. Fossil is sensitive to this. For
219219
instance, Fossil will not collapse double slashes down to a single
220220
slash, as some other HTTP servers will.
221221
222
+
223
+## <a name="large-uv"></a> Allowing Large Unversioned Files
224
+
225
+By default, nginx only accepts HTTP messages [up to a
226
+meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)
227
+in size. Fossil chunks its sync protocol such that this is not normally
228
+a problem, but when sending [unversioned content][uv], it uses a single
229
+message for the entire file. Therefore, if you will be storing files
230
+larger than this limit as unversioned content, you need to raise the
231
+limit. Within the `location` block:
232
+
233
+ # Allow large unversioned file uploads, such as PDFs
234
+ client_max_body_size 20M;
235
+
236
+[uv]: ../../unvers.wiki
237
+
222238
223239
## <a name="fail2ban"></a> Integrating `fail2ban`
224240
225
-You can have `fail2ban` recognize attacks and automatically block them,
226
-but the stock configuration doesn’t work with our Fossil setup above, so
227
-we have to do a bit of local adjustment.
241
+One of the nice things that falls out of proxying Fossil behind nginx is
242
+that it makes it easier to configure `fail2ban` to recognize attacks on
243
+Fossil and automatically block them. Fossil logs the sorts of errors we
244
+want to detect, but it does so in places like the repository’s admin
245
+log, a SQL table, which `fail2ban` doesn’t know how to query. By putting
246
+Fossil behind an nginx proxy, we convert these failures to log file
247
+form, which `fail2ban` is designed to handle.
228248
229
-First, install it:
249
+First, install `fail2ban`, if you haven’t already:
230250
231251
sudo apt install fail2ban
232252
233
-Out of the box, you get SSH monitoring only. There are nginx monitors
234
-included with the package, but they don’t look in the right places for
235
-the right things. We’d like it to react to Fossil `/login` failures, for
236
-example. Put the following into
253
+We’d like `fail2ban` to react to Fossil `/login` failures. The stock
254
+configuration of `fail2ban` only detects a few common sorts of SSH
255
+attacks by default, and its included (but disabled) nginx attack
256
+detectors don’t include one that knows how to detect an attack on
257
+Fossil. We have to teach it by putting the following into
237258
`/etc/fail2ban/filter.d/nginx-fossil-login.conf`:
238259
239260
[Definition]
240261
failregex = ^<HOST> - .*POST .*/login HTTP/..." 401
241262
242263
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -217,25 +217,46 @@
217 The most common thing people get wrong when hand-rolling a configuration
218 like this is to get the slashes wrong. Fossil is sensitive to this. For
219 instance, Fossil will not collapse double slashes down to a single
220 slash, as some other HTTP servers will.
221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
223 ## <a name="fail2ban"></a> Integrating `fail2ban`
224
225 You can have `fail2ban` recognize attacks and automatically block them,
226 but the stock configuration doesn’t work with our Fossil setup above, so
227 we have to do a bit of local adjustment.
 
 
 
 
228
229 First, install it:
230
231 sudo apt install fail2ban
232
233 Out of the box, you get SSH monitoring only. There are nginx monitors
234 included with the package, but they don’t look in the right places for
235 the right things. We’d like it to react to Fossil `/login` failures, for
236 example. Put the following into
 
237 `/etc/fail2ban/filter.d/nginx-fossil-login.conf`:
238
239 [Definition]
240 failregex = ^<HOST> - .*POST .*/login HTTP/..." 401
241
242
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -217,25 +217,46 @@
217 The most common thing people get wrong when hand-rolling a configuration
218 like this is to get the slashes wrong. Fossil is sensitive to this. For
219 instance, Fossil will not collapse double slashes down to a single
220 slash, as some other HTTP servers will.
221
222
223 ## <a name="large-uv"></a> Allowing Large Unversioned Files
224
225 By default, nginx only accepts HTTP messages [up to a
226 meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)
227 in size. Fossil chunks its sync protocol such that this is not normally
228 a problem, but when sending [unversioned content][uv], it uses a single
229 message for the entire file. Therefore, if you will be storing files
230 larger than this limit as unversioned content, you need to raise the
231 limit. Within the `location` block:
232
233 # Allow large unversioned file uploads, such as PDFs
234 client_max_body_size 20M;
235
236 [uv]: ../../unvers.wiki
237
238
239 ## <a name="fail2ban"></a> Integrating `fail2ban`
240
241 One of the nice things that falls out of proxying Fossil behind nginx is
242 that it makes it easier to configure `fail2ban` to recognize attacks on
243 Fossil and automatically block them. Fossil logs the sorts of errors we
244 want to detect, but it does so in places like the repository’s admin
245 log, a SQL table, which `fail2ban` doesn’t know how to query. By putting
246 Fossil behind an nginx proxy, we convert these failures to log file
247 form, which `fail2ban` is designed to handle.
248
249 First, install `fail2ban`, if you haven’t already:
250
251 sudo apt install fail2ban
252
253 We’d like `fail2ban` to react to Fossil `/login` failures. The stock
254 configuration of `fail2ban` only detects a few common sorts of SSH
255 attacks by default, and its included (but disabled) nginx attack
256 detectors don’t include one that knows how to detect an attack on
257 Fossil. We have to teach it by putting the following into
258 `/etc/fail2ban/filter.d/nginx-fossil-login.conf`:
259
260 [Definition]
261 failregex = ^<HOST> - .*POST .*/login HTTP/..." 401
262
263
--- www/server/openbsd/fastcgi.md
+++ www/server/openbsd/fastcgi.md
@@ -159,15 +159,27 @@
159159
root "/acme"
160160
request strip 2
161161
}
162162
}
163163
```
164
+
165
+[The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server
166
+is 1 MiB. Fossil chunks its sync protocol such that this is not
167
+normally a problem, but when sending [unversioned content][uv], it uses
168
+a single message for the entire file. Therefore, if you will be storing
169
+files larger than this limit as unversioned content, you need to raise
170
+the limit as we’ve done above with the “`connection max request body`”
171
+setting, raising the limit to 100 MiB.
172
+
173
+[dlim]: https://man.openbsd.org/httpd.conf.5#connection
174
+[uv]: ../../unvers.wiki
164175
165176
**NOTE:** If not already in possession of a HTTPS certificate, comment
166177
out the `https` server block and proceed to securing a free
167178
[Let's Encrypt Certificate](#letsencrypt); otherwise skip to
168179
[Start `httpd`](#starthttpd).
180
+
169181
170182
## <a name="letsencrypt"></a>Let's Encrypt Certificate
171183
172184
In order for `httpd` to serve HTTPS, secure a free certificate from
173185
Let's Encrypt using `acme-client`. Before issuing the request, however,
174186
--- www/server/openbsd/fastcgi.md
+++ www/server/openbsd/fastcgi.md
@@ -159,15 +159,27 @@
159 root "/acme"
160 request strip 2
161 }
162 }
163 ```
 
 
 
 
 
 
 
 
 
 
 
164
165 **NOTE:** If not already in possession of a HTTPS certificate, comment
166 out the `https` server block and proceed to securing a free
167 [Let's Encrypt Certificate](#letsencrypt); otherwise skip to
168 [Start `httpd`](#starthttpd).
 
169
170 ## <a name="letsencrypt"></a>Let's Encrypt Certificate
171
172 In order for `httpd` to serve HTTPS, secure a free certificate from
173 Let's Encrypt using `acme-client`. Before issuing the request, however,
174
--- www/server/openbsd/fastcgi.md
+++ www/server/openbsd/fastcgi.md
@@ -159,15 +159,27 @@
159 root "/acme"
160 request strip 2
161 }
162 }
163 ```
164
165 [The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server
166 is 1 MiB. Fossil chunks its sync protocol such that this is not
167 normally a problem, but when sending [unversioned content][uv], it uses
168 a single message for the entire file. Therefore, if you will be storing
169 files larger than this limit as unversioned content, you need to raise
170 the limit as we’ve done above with the “`connection max request body`”
171 setting, raising the limit to 100 MiB.
172
173 [dlim]: https://man.openbsd.org/httpd.conf.5#connection
174 [uv]: ../../unvers.wiki
175
176 **NOTE:** If not already in possession of a HTTPS certificate, comment
177 out the `https` server block and proceed to securing a free
178 [Let's Encrypt Certificate](#letsencrypt); otherwise skip to
179 [Start `httpd`](#starthttpd).
180
181
182 ## <a name="letsencrypt"></a>Let's Encrypt Certificate
183
184 In order for `httpd` to serve HTTPS, secure a free certificate from
185 Let's Encrypt using `acme-client`. Before issuing the request, however,
186
+1 -1
--- www/sync.wiki
+++ www/sync.wiki
@@ -12,11 +12,11 @@
1212
repositories so that all repositories have copies of all artifacts. Because
1313
artifacts are unordered, the order in which artifacts are received
1414
is unimportant. It is assumed that the hash names
1515
of artifacts are unique - that every artifact has a different hash.
1616
To a first approximation, synchronization proceeds by sharing lists
17
-hash values for available artifacts, then sharing the content of artifacts
17
+of hashes for available artifacts, then sharing the content of artifacts
1818
whose names are missing from one side or the other of the connection.
1919
In practice, a repository might contain millions of artifacts. The list of
2020
hash names for this many artifacts can be large. So optimizations are
2121
employed that usually reduce the number of hashes that need to be
2222
shared to a few hundred.</p>
2323
--- www/sync.wiki
+++ www/sync.wiki
@@ -12,11 +12,11 @@
12 repositories so that all repositories have copies of all artifacts. Because
13 artifacts are unordered, the order in which artifacts are received
14 is unimportant. It is assumed that the hash names
15 of artifacts are unique - that every artifact has a different hash.
16 To a first approximation, synchronization proceeds by sharing lists
17 hash values for available artifacts, then sharing the content of artifacts
18 whose names are missing from one side or the other of the connection.
19 In practice, a repository might contain millions of artifacts. The list of
20 hash names for this many artifacts can be large. So optimizations are
21 employed that usually reduce the number of hashes that need to be
22 shared to a few hundred.</p>
23
--- www/sync.wiki
+++ www/sync.wiki
@@ -12,11 +12,11 @@
12 repositories so that all repositories have copies of all artifacts. Because
13 artifacts are unordered, the order in which artifacts are received
14 is unimportant. It is assumed that the hash names
15 of artifacts are unique - that every artifact has a different hash.
16 To a first approximation, synchronization proceeds by sharing lists
17 of hashes for available artifacts, then sharing the content of artifacts
18 whose names are missing from one side or the other of the connection.
19 In practice, a repository might contain millions of artifacts. The list of
20 hash names for this many artifacts can be large. So optimizations are
21 employed that usually reduce the number of hashes that need to be
22 shared to a few hundred.</p>
23

Keyboard Shortcuts

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