Fossil SCM

Merge from trunk

george 2021-03-13 21:39 wiki-history merge
Commit 1a403a7a0c142074a3d63884bbd0d12d9efcb0f74ce383616850449b3043d18f
80 files changed +19 +2 -1 -4 +7 +16 -2 +24 -9 +1 -1 +1 -1 +1 -1 -3 +1 -2 +25 -4 +1 +2 -2 +12 +13 -13 +1 -1 +1 -1 +1 -1 +4 -3 +1 +3 +23 -9 +1 -1 +2 -2 +4 -4 +1 -1 +7 -8 +2 +2 +47 -12 +2 -2 +18 +1 -1 +10 -27 +1 +1 +1 -1 +1 +1 -1 +12 -12 +1 -1 +5 -5 +6 -6 +55 -3 +4 -4 +1 -1 +205 -96 +54 -6 +23 -4 +346 -146 +23 -12 +71 -37 -1 +1 -1 -1 +1 -1 +1 -1 +1 -1 +1 -1 +3 -3 +3 -3 +1 -1 +12 +1 +2 +14 -6 +82 -56 +47 -13 +5 -4 +18 -1 +50 -46 +250 +69 -27 +2 -2 +1 -1 +17 -10 +109 -342 +39 -11 +14
+19
--- auto.def
+++ auto.def
@@ -1,7 +1,9 @@
11
# System autoconfiguration. Try: ./configure --help
22
3
+# This must be above "options" below because it implicitly brings in the
4
+# default Autosetup options, things like --prefix.
35
use cc cc-lib
46
57
options {
68
with-openssl:path|auto|tree|none
79
=> {Look for OpenSSL in the given path, automatically, in the source tree, or none}
@@ -17,17 +19,31 @@
1719
with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism}
1820
with-tcl-private-stubs=0
1921
=> {Enable Tcl integration via private stubs mechanism}
2022
with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"}
2123
with-see=0 => {Enable the SQLite Encryption Extension (SEE)}
24
+ print-minimum-sqlite-version=0
25
+ => {print the minimum SQLite version number required, and exit}
2226
internal-sqlite=1 => {Don't use the internal SQLite, use the system one}
2327
static=0 => {Link a static executable}
2428
fusefs=1 => {Disable the Fuse Filesystem}
2529
fossil-debug=0 => {Build with fossil debugging enabled}
2630
no-opt=0 => {Build without optimization}
2731
json=0 => {Build with fossil JSON API enabled}
2832
}
33
+
34
+# Update the minimum required SQLite version number here, and also
35
+# in src/main.c near the sqlite3_libversion_number() call. Take care
36
+# that both places agree!
37
+define MINIMUM_SQLITE_VERSION "3.35.0"
38
+
39
+# This is useful for people wanting Fossil to use an external SQLite library
40
+# to compare the one they have against the minimum required
41
+if {[opt-bool print-minimum-sqlite-version]} {
42
+ puts [get-define MINIMUM_SQLITE_VERSION]
43
+ exit 0
44
+}
2945
3046
# sqlite wants these types if possible
3147
cc-with {-includes {stdint.h inttypes.h}} {
3248
cc-check-types uint32_t uint16_t int16_t uint8_t
3349
}
@@ -154,17 +170,20 @@
154170
find_system_sqlite
155171
156172
proc test_system_sqlite {} {
157173
# Check compatibility of the system SQLite library by running the sqlcompttest.c
158174
# program in the source tree
175
+ # passes MINIMUM_SQLITE_VERSION set at the top of this file to sqlcompttest.c
159176
#
160177
set cmdline {}
161178
lappend cmdline {*}[get-define CCACHE]
162179
lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
163180
lappend cmdline $::autosetup(dir)/../src/sqlcompattest.c -o conftest__
164181
lappend cmdline {*}[get-define LDFLAGS]
165182
lappend cmdline {*}[get-define LIBS]
183
+ set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
184
+ lappend cmdline {*}[set sqlite-version]
166185
set ok 1
167186
set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
168187
if {$err} {
169188
configlog "Failed: [join $cmdline]"
170189
if {[string length $result]>0} {configlog $result}
171190
--- auto.def
+++ auto.def
@@ -1,7 +1,9 @@
1 # System autoconfiguration. Try: ./configure --help
2
 
 
3 use cc cc-lib
4
5 options {
6 with-openssl:path|auto|tree|none
7 => {Look for OpenSSL in the given path, automatically, in the source tree, or none}
@@ -17,17 +19,31 @@
17 with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism}
18 with-tcl-private-stubs=0
19 => {Enable Tcl integration via private stubs mechanism}
20 with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"}
21 with-see=0 => {Enable the SQLite Encryption Extension (SEE)}
 
 
22 internal-sqlite=1 => {Don't use the internal SQLite, use the system one}
23 static=0 => {Link a static executable}
24 fusefs=1 => {Disable the Fuse Filesystem}
25 fossil-debug=0 => {Build with fossil debugging enabled}
26 no-opt=0 => {Build without optimization}
27 json=0 => {Build with fossil JSON API enabled}
28 }
 
 
 
 
 
 
 
 
 
 
 
 
29
30 # sqlite wants these types if possible
31 cc-with {-includes {stdint.h inttypes.h}} {
32 cc-check-types uint32_t uint16_t int16_t uint8_t
33 }
@@ -154,17 +170,20 @@
154 find_system_sqlite
155
156 proc test_system_sqlite {} {
157 # Check compatibility of the system SQLite library by running the sqlcompttest.c
158 # program in the source tree
 
159 #
160 set cmdline {}
161 lappend cmdline {*}[get-define CCACHE]
162 lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
163 lappend cmdline $::autosetup(dir)/../src/sqlcompattest.c -o conftest__
164 lappend cmdline {*}[get-define LDFLAGS]
165 lappend cmdline {*}[get-define LIBS]
 
 
166 set ok 1
167 set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
168 if {$err} {
169 configlog "Failed: [join $cmdline]"
170 if {[string length $result]>0} {configlog $result}
171
--- auto.def
+++ auto.def
@@ -1,7 +1,9 @@
1 # System autoconfiguration. Try: ./configure --help
2
3 # This must be above "options" below because it implicitly brings in the
4 # default Autosetup options, things like --prefix.
5 use cc cc-lib
6
7 options {
8 with-openssl:path|auto|tree|none
9 => {Look for OpenSSL in the given path, automatically, in the source tree, or none}
@@ -17,17 +19,31 @@
19 with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism}
20 with-tcl-private-stubs=0
21 => {Enable Tcl integration via private stubs mechanism}
22 with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"}
23 with-see=0 => {Enable the SQLite Encryption Extension (SEE)}
24 print-minimum-sqlite-version=0
25 => {print the minimum SQLite version number required, and exit}
26 internal-sqlite=1 => {Don't use the internal SQLite, use the system one}
27 static=0 => {Link a static executable}
28 fusefs=1 => {Disable the Fuse Filesystem}
29 fossil-debug=0 => {Build with fossil debugging enabled}
30 no-opt=0 => {Build without optimization}
31 json=0 => {Build with fossil JSON API enabled}
32 }
33
34 # Update the minimum required SQLite version number here, and also
35 # in src/main.c near the sqlite3_libversion_number() call. Take care
36 # that both places agree!
37 define MINIMUM_SQLITE_VERSION "3.35.0"
38
39 # This is useful for people wanting Fossil to use an external SQLite library
40 # to compare the one they have against the minimum required
41 if {[opt-bool print-minimum-sqlite-version]} {
42 puts [get-define MINIMUM_SQLITE_VERSION]
43 exit 0
44 }
45
46 # sqlite wants these types if possible
47 cc-with {-includes {stdint.h inttypes.h}} {
48 cc-check-types uint32_t uint16_t int16_t uint8_t
49 }
@@ -154,17 +170,20 @@
170 find_system_sqlite
171
172 proc test_system_sqlite {} {
173 # Check compatibility of the system SQLite library by running the sqlcompttest.c
174 # program in the source tree
175 # passes MINIMUM_SQLITE_VERSION set at the top of this file to sqlcompttest.c
176 #
177 set cmdline {}
178 lappend cmdline {*}[get-define CCACHE]
179 lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
180 lappend cmdline $::autosetup(dir)/../src/sqlcompattest.c -o conftest__
181 lappend cmdline {*}[get-define LDFLAGS]
182 lappend cmdline {*}[get-define LIBS]
183 set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
184 lappend cmdline {*}[set sqlite-version]
185 set ok 1
186 set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
187 if {$err} {
188 configlog "Failed: [join $cmdline]"
189 if {[string length $result]>0} {configlog $result}
190
+2 -1
--- skins/README.md
+++ skins/README.md
@@ -30,11 +30,12 @@
3030
4. Edit the BuiltinSkin[] array near the top of the src/skins.c source
3131
file so that it describes and references the "newskin" skin.
3232
3333
5. Type "make" to rebuild.
3434
35
-See the [custom skin documentation](../www/customskin.md) for more information.
35
+See the [custom skin documentation](/doc/$CURRENT/www/customskin.md) for
36
+more information.
3637
3738
Development Hints
3839
-----------------
3940
4041
One way to develop a new skin is to copy the baseline files (css.txt,
4142
--- skins/README.md
+++ skins/README.md
@@ -30,11 +30,12 @@
30 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source
31 file so that it describes and references the "newskin" skin.
32
33 5. Type "make" to rebuild.
34
35 See the [custom skin documentation](../www/customskin.md) for more information.
 
36
37 Development Hints
38 -----------------
39
40 One way to develop a new skin is to copy the baseline files (css.txt,
41
--- skins/README.md
+++ skins/README.md
@@ -30,11 +30,12 @@
30 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source
31 file so that it describes and references the "newskin" skin.
32
33 5. Type "make" to rebuild.
34
35 See the [custom skin documentation](/doc/$CURRENT/www/customskin.md) for
36 more information.
37
38 Development Hints
39 -----------------
40
41 One way to develop a new skin is to copy the baseline files (css.txt,
42
--- skins/darkmode/css.txt
+++ skins/darkmode/css.txt
@@ -134,14 +134,10 @@
134134
color: #333;
135135
border-color: #888;
136136
outline: 0
137137
}
138138
139
-div.mainmenu a.active {
140
- opacity: 0.8;
141
-}
142
-
143139
/* All page content from the bottom of the menu or submenu down to
144140
** the footer */
145141
div.content {
146142
padding: 0ex 1ex 1ex 1ex;
147143
}
148144
--- skins/darkmode/css.txt
+++ skins/darkmode/css.txt
@@ -134,14 +134,10 @@
134 color: #333;
135 border-color: #888;
136 outline: 0
137 }
138
139 div.mainmenu a.active {
140 opacity: 0.8;
141 }
142
143 /* All page content from the bottom of the menu or submenu down to
144 ** the footer */
145 div.content {
146 padding: 0ex 1ex 1ex 1ex;
147 }
148
--- skins/darkmode/css.txt
+++ skins/darkmode/css.txt
@@ -134,14 +134,10 @@
134 color: #333;
135 border-color: #888;
136 outline: 0
137 }
138
 
 
 
 
139 /* All page content from the bottom of the menu or submenu down to
140 ** the footer */
141 div.content {
142 padding: 0ex 1ex 1ex 1ex;
143 }
144
--- skins/khaki/css.txt
+++ skins/khaki/css.txt
@@ -163,5 +163,12 @@
163163
table.label-value th {
164164
vertical-align: top;
165165
text-align: right;
166166
padding: 0.2ex 2ex;
167167
}
168
+
169
+div.forumPostBody blockquote {
170
+ border-width: 1pt;
171
+ border-radius: 0.25em;
172
+ border-style: solid;
173
+ padding: 0 0.5em;
174
+}
168175
--- skins/khaki/css.txt
+++ skins/khaki/css.txt
@@ -163,5 +163,12 @@
163 table.label-value th {
164 vertical-align: top;
165 text-align: right;
166 padding: 0.2ex 2ex;
167 }
 
 
 
 
 
 
 
168
--- skins/khaki/css.txt
+++ skins/khaki/css.txt
@@ -163,5 +163,12 @@
163 table.label-value th {
164 vertical-align: top;
165 text-align: right;
166 padding: 0.2ex 2ex;
167 }
168
169 div.forumPostBody blockquote {
170 border-width: 1pt;
171 border-radius: 0.25em;
172 border-style: solid;
173 padding: 0 0.5em;
174 }
175
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -28,11 +28,11 @@
2828
-ms-text-size-adjust: none;
2929
-webkit-text-size-adjust: none;
3030
}
3131
3232
a {
33
- color: #07e;
33
+ color: #40a0ff;
3434
}
3535
3636
a:hover {
3737
font-weight: bold;
3838
}
@@ -115,11 +115,11 @@
115115
vertical-align: top;
116116
white-space: nowrap;
117117
}
118118
119119
div.title {
120
- color: #07e;
120
+ color: #3297f9;
121121
font-family: Verdana, sans-serif;
122122
font-weight: bold;
123123
font-size: 2.5rem;
124124
padding: 0.5rem;
125125
text-align: center;
@@ -1124,5 +1124,19 @@
11241124
.fossil-PopupWidget,
11251125
.fossil-tooltip.help-buttonlet-content {
11261126
background-color: #111;
11271127
border: 1px solid rgba(255,255,255,0.5);
11281128
}
1129
+
1130
+div.forumSel {
1131
+ background-color: #663399;
1132
+}
1133
+div.forumPostBody blockquote {
1134
+ border-width: 1pt;
1135
+ border-style: solid;
1136
+ padding: 0 0.5em;
1137
+ border-radius: 0.25em;
1138
+}
1139
+
1140
+.debug {
1141
+ color: black;
1142
+}
11291143
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -28,11 +28,11 @@
28 -ms-text-size-adjust: none;
29 -webkit-text-size-adjust: none;
30 }
31
32 a {
33 color: #07e;
34 }
35
36 a:hover {
37 font-weight: bold;
38 }
@@ -115,11 +115,11 @@
115 vertical-align: top;
116 white-space: nowrap;
117 }
118
119 div.title {
120 color: #07e;
121 font-family: Verdana, sans-serif;
122 font-weight: bold;
123 font-size: 2.5rem;
124 padding: 0.5rem;
125 text-align: center;
@@ -1124,5 +1124,19 @@
1124 .fossil-PopupWidget,
1125 .fossil-tooltip.help-buttonlet-content {
1126 background-color: #111;
1127 border: 1px solid rgba(255,255,255,0.5);
1128 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1129
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -28,11 +28,11 @@
28 -ms-text-size-adjust: none;
29 -webkit-text-size-adjust: none;
30 }
31
32 a {
33 color: #40a0ff;
34 }
35
36 a:hover {
37 font-weight: bold;
38 }
@@ -115,11 +115,11 @@
115 vertical-align: top;
116 white-space: nowrap;
117 }
118
119 div.title {
120 color: #3297f9;
121 font-family: Verdana, sans-serif;
122 font-weight: bold;
123 font-size: 2.5rem;
124 padding: 0.5rem;
125 text-align: center;
@@ -1124,5 +1124,19 @@
1124 .fossil-PopupWidget,
1125 .fossil-tooltip.help-buttonlet-content {
1126 background-color: #111;
1127 border: 1px solid rgba(255,255,255,0.5);
1128 }
1129
1130 div.forumSel {
1131 background-color: #663399;
1132 }
1133 div.forumPostBody blockquote {
1134 border-width: 1pt;
1135 border-style: solid;
1136 padding: 0 0.5em;
1137 border-radius: 0.25em;
1138 }
1139
1140 .debug {
1141 color: black;
1142 }
1143
+24 -9
--- src/add.c
+++ src/add.c
@@ -363,12 +363,12 @@
363363
** be treated case sensitive or not. If the option is not given, the default
364364
** depends on the global setting, or the operating system default, if not set.
365365
**
366366
** Options:
367367
**
368
-** --case-sensitive BOOL Override the case-sensitive setting.
369
-** --dotfiles include files beginning with a dot (".")
368
+** --case-sensitive BOOL Override the case-sensitive setting
369
+** --dotfiles Include files beginning with a dot (".")
370370
** -f|--force Add files without prompting
371371
** --ignore CSG Ignore unmanaged files matching patterns from
372372
** the Comma Separated Glob (CSG) pattern list
373373
** --clean CSG Also ignore files matching patterns from
374374
** the Comma Separated Glob (CSG) list
@@ -375,14 +375,17 @@
375375
** --reset Reset the ADDED state of a checkout, such
376376
** that all newly-added (but not yet committed)
377377
** files are no longer added. No flags other
378378
** than --verbose and --dry-run may be used
379379
** with --reset.
380
+** --allow-reserved Permit filenames which are reserved on
381
+** Windows platforms. Such files cannot be
382
+** checked out on Windows, so use with care.
380383
**
381384
** The following options are only valid with --reset:
382
-** -v|--verbose Outputs information about each --reset file.
383
-** -n|--dry-run Display instead of run actions.
385
+** -v|--verbose Output information about each --reset file
386
+** -n|--dry-run Display instead of run actions
384387
**
385388
** See also: [[addremove]], [[rm]]
386389
*/
387390
void add_cmd(void){
388391
int i; /* Loop counter */
@@ -391,10 +394,11 @@
391394
const char *zCleanFlag; /* The --clean option or clean-glob setting */
392395
const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */
393396
Glob *pIgnore, *pClean; /* Ignore everything matching the glob patterns */
394397
unsigned scanFlags = 0; /* Flags passed to vfile_scan() */
395398
int forceFlag;
399
+ int allowReservedFlag = 0; /* --allow-reserved flag */
396400
397401
if(0!=find_option("reset",0,0)){
398402
int const verboseFlag = find_option("verbose","v",0)!=0;
399403
int const dryRunFlag = find_option("dry-run","n",0)!=0;
400404
db_must_be_within_tree();
@@ -405,10 +409,11 @@
405409
406410
zCleanFlag = find_option("clean",0,1);
407411
zIgnoreFlag = find_option("ignore",0,1);
408412
forceFlag = find_option("force","f",0)!=0;
409413
if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
414
+ allowReservedFlag = find_option("allow-reserved",0,0)!=0;
410415
411416
/* We should be done with options.. */
412417
verify_all_options();
413418
414419
db_must_be_within_tree();
@@ -429,17 +434,27 @@
429434
430435
/* Load the names of all files that are to be added into sfile temp table */
431436
for(i=2; i<g.argc; i++){
432437
char *zName;
433438
int isDir;
434
- Blob fullName;
439
+ Blob fullName = empty_blob;
435440
436441
/* file_tree_name() throws a fatal error if g.argv[i] is outside of the
437442
** checkout. */
438443
file_tree_name(g.argv[i], &fullName, 0, 1);
444
+ if(0==allowReservedFlag
445
+ && 0!=file_is_win_reserved(blob_str(&fullName))){
446
+ /* Note that the 'add' internal machinery already _silently_
447
+ ** skips over any names for which file_is_reserved_name()
448
+ ** returns true or which is in the fossil_reserved_name()
449
+ ** list. We do not need to warn for those, as they're outright
450
+ ** verboten. */
451
+ fossil_fatal("Filename is reserved: %b\n"
452
+ "Use --allow-reserved to permit "
453
+ "reserved filenames.", &fullName);
454
+ }
439455
blob_reset(&fullName);
440
-
441456
file_canonical_name(g.argv[i], &fullName, 0);
442457
zName = blob_str(&fullName);
443458
isDir = file_isdir(zName, RepoFILE);
444459
if( isDir==1 ){
445460
vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
@@ -989,13 +1004,13 @@
9891004
** as well. This does NOT apply to the 'rename' command.
9901005
**
9911006
** Options:
9921007
** --soft Skip moving files within the checkout.
9931008
** This supersedes the --hard option.
994
-** --hard Move files within the checkout.
995
-** --case-sensitive BOOL Override the case-sensitive setting.
996
-** -n|--dry-run If given, display instead of run actions.
1009
+** --hard Move files within the checkout
1010
+** --case-sensitive BOOL Override the case-sensitive setting
1011
+** -n|--dry-run If given, display instead of run actions
9971012
**
9981013
** See also: [[changes]], [[status]]
9991014
*/
10001015
void mv_cmd(void){
10011016
int i;
10021017
--- src/add.c
+++ src/add.c
@@ -363,12 +363,12 @@
363 ** be treated case sensitive or not. If the option is not given, the default
364 ** depends on the global setting, or the operating system default, if not set.
365 **
366 ** Options:
367 **
368 ** --case-sensitive BOOL Override the case-sensitive setting.
369 ** --dotfiles include files beginning with a dot (".")
370 ** -f|--force Add files without prompting
371 ** --ignore CSG Ignore unmanaged files matching patterns from
372 ** the Comma Separated Glob (CSG) pattern list
373 ** --clean CSG Also ignore files matching patterns from
374 ** the Comma Separated Glob (CSG) list
@@ -375,14 +375,17 @@
375 ** --reset Reset the ADDED state of a checkout, such
376 ** that all newly-added (but not yet committed)
377 ** files are no longer added. No flags other
378 ** than --verbose and --dry-run may be used
379 ** with --reset.
 
 
 
380 **
381 ** The following options are only valid with --reset:
382 ** -v|--verbose Outputs information about each --reset file.
383 ** -n|--dry-run Display instead of run actions.
384 **
385 ** See also: [[addremove]], [[rm]]
386 */
387 void add_cmd(void){
388 int i; /* Loop counter */
@@ -391,10 +394,11 @@
391 const char *zCleanFlag; /* The --clean option or clean-glob setting */
392 const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */
393 Glob *pIgnore, *pClean; /* Ignore everything matching the glob patterns */
394 unsigned scanFlags = 0; /* Flags passed to vfile_scan() */
395 int forceFlag;
 
396
397 if(0!=find_option("reset",0,0)){
398 int const verboseFlag = find_option("verbose","v",0)!=0;
399 int const dryRunFlag = find_option("dry-run","n",0)!=0;
400 db_must_be_within_tree();
@@ -405,10 +409,11 @@
405
406 zCleanFlag = find_option("clean",0,1);
407 zIgnoreFlag = find_option("ignore",0,1);
408 forceFlag = find_option("force","f",0)!=0;
409 if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
 
410
411 /* We should be done with options.. */
412 verify_all_options();
413
414 db_must_be_within_tree();
@@ -429,17 +434,27 @@
429
430 /* Load the names of all files that are to be added into sfile temp table */
431 for(i=2; i<g.argc; i++){
432 char *zName;
433 int isDir;
434 Blob fullName;
435
436 /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
437 ** checkout. */
438 file_tree_name(g.argv[i], &fullName, 0, 1);
 
 
 
 
 
 
 
 
 
 
 
439 blob_reset(&fullName);
440
441 file_canonical_name(g.argv[i], &fullName, 0);
442 zName = blob_str(&fullName);
443 isDir = file_isdir(zName, RepoFILE);
444 if( isDir==1 ){
445 vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
@@ -989,13 +1004,13 @@
989 ** as well. This does NOT apply to the 'rename' command.
990 **
991 ** Options:
992 ** --soft Skip moving files within the checkout.
993 ** This supersedes the --hard option.
994 ** --hard Move files within the checkout.
995 ** --case-sensitive BOOL Override the case-sensitive setting.
996 ** -n|--dry-run If given, display instead of run actions.
997 **
998 ** See also: [[changes]], [[status]]
999 */
1000 void mv_cmd(void){
1001 int i;
1002
--- src/add.c
+++ src/add.c
@@ -363,12 +363,12 @@
363 ** be treated case sensitive or not. If the option is not given, the default
364 ** depends on the global setting, or the operating system default, if not set.
365 **
366 ** Options:
367 **
368 ** --case-sensitive BOOL Override the case-sensitive setting
369 ** --dotfiles Include files beginning with a dot (".")
370 ** -f|--force Add files without prompting
371 ** --ignore CSG Ignore unmanaged files matching patterns from
372 ** the Comma Separated Glob (CSG) pattern list
373 ** --clean CSG Also ignore files matching patterns from
374 ** the Comma Separated Glob (CSG) list
@@ -375,14 +375,17 @@
375 ** --reset Reset the ADDED state of a checkout, such
376 ** that all newly-added (but not yet committed)
377 ** files are no longer added. No flags other
378 ** than --verbose and --dry-run may be used
379 ** with --reset.
380 ** --allow-reserved Permit filenames which are reserved on
381 ** Windows platforms. Such files cannot be
382 ** checked out on Windows, so use with care.
383 **
384 ** The following options are only valid with --reset:
385 ** -v|--verbose Output information about each --reset file
386 ** -n|--dry-run Display instead of run actions
387 **
388 ** See also: [[addremove]], [[rm]]
389 */
390 void add_cmd(void){
391 int i; /* Loop counter */
@@ -391,10 +394,11 @@
394 const char *zCleanFlag; /* The --clean option or clean-glob setting */
395 const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */
396 Glob *pIgnore, *pClean; /* Ignore everything matching the glob patterns */
397 unsigned scanFlags = 0; /* Flags passed to vfile_scan() */
398 int forceFlag;
399 int allowReservedFlag = 0; /* --allow-reserved flag */
400
401 if(0!=find_option("reset",0,0)){
402 int const verboseFlag = find_option("verbose","v",0)!=0;
403 int const dryRunFlag = find_option("dry-run","n",0)!=0;
404 db_must_be_within_tree();
@@ -405,10 +409,11 @@
409
410 zCleanFlag = find_option("clean",0,1);
411 zIgnoreFlag = find_option("ignore",0,1);
412 forceFlag = find_option("force","f",0)!=0;
413 if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
414 allowReservedFlag = find_option("allow-reserved",0,0)!=0;
415
416 /* We should be done with options.. */
417 verify_all_options();
418
419 db_must_be_within_tree();
@@ -429,17 +434,27 @@
434
435 /* Load the names of all files that are to be added into sfile temp table */
436 for(i=2; i<g.argc; i++){
437 char *zName;
438 int isDir;
439 Blob fullName = empty_blob;
440
441 /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
442 ** checkout. */
443 file_tree_name(g.argv[i], &fullName, 0, 1);
444 if(0==allowReservedFlag
445 && 0!=file_is_win_reserved(blob_str(&fullName))){
446 /* Note that the 'add' internal machinery already _silently_
447 ** skips over any names for which file_is_reserved_name()
448 ** returns true or which is in the fossil_reserved_name()
449 ** list. We do not need to warn for those, as they're outright
450 ** verboten. */
451 fossil_fatal("Filename is reserved: %b\n"
452 "Use --allow-reserved to permit "
453 "reserved filenames.", &fullName);
454 }
455 blob_reset(&fullName);
 
456 file_canonical_name(g.argv[i], &fullName, 0);
457 zName = blob_str(&fullName);
458 isDir = file_isdir(zName, RepoFILE);
459 if( isDir==1 ){
460 vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
@@ -989,13 +1004,13 @@
1004 ** as well. This does NOT apply to the 'rename' command.
1005 **
1006 ** Options:
1007 ** --soft Skip moving files within the checkout.
1008 ** This supersedes the --hard option.
1009 ** --hard Move files within the checkout
1010 ** --case-sensitive BOOL Override the case-sensitive setting
1011 ** -n|--dry-run If given, display instead of run actions
1012 **
1013 ** See also: [[changes]], [[status]]
1014 */
1015 void mv_cmd(void){
1016 int i;
1017
+1 -1
--- src/ajax.c
+++ src/ajax.c
@@ -66,11 +66,11 @@
6666
}
6767
}
6868
6969
/*
7070
** Returns a value from the ajax_render_modes enum, based on the
71
-** given mime type string (which may be NULL), defaulting to
71
+** given mimetype string (which may be NULL), defaulting to
7272
** AJAX_RENDER_PLAIN_TEXT.
7373
*/
7474
int ajax_render_mode_for_mimetype(const char * zMimetype){
7575
int rc = AJAX_RENDER_PLAIN_TEXT;
7676
if( zMimetype ){
7777
--- src/ajax.c
+++ src/ajax.c
@@ -66,11 +66,11 @@
66 }
67 }
68
69 /*
70 ** Returns a value from the ajax_render_modes enum, based on the
71 ** given mime type string (which may be NULL), defaulting to
72 ** AJAX_RENDER_PLAIN_TEXT.
73 */
74 int ajax_render_mode_for_mimetype(const char * zMimetype){
75 int rc = AJAX_RENDER_PLAIN_TEXT;
76 if( zMimetype ){
77
--- src/ajax.c
+++ src/ajax.c
@@ -66,11 +66,11 @@
66 }
67 }
68
69 /*
70 ** Returns a value from the ajax_render_modes enum, based on the
71 ** given mimetype string (which may be NULL), defaulting to
72 ** AJAX_RENDER_PLAIN_TEXT.
73 */
74 int ajax_render_mode_for_mimetype(const char * zMimetype){
75 int rc = AJAX_RENDER_PLAIN_TEXT;
76 if( zMimetype ){
77
+1 -1
--- src/alerts.c
+++ src/alerts.c
@@ -1020,11 +1020,11 @@
10201020
** configuration. Options:
10211021
**
10221022
** --body FILENAME
10231023
** --smtp-trace
10241024
** --stdout
1025
-** --subject|-S SUBJECT
1025
+** -S|--subject SUBJECT
10261026
**
10271027
** unsubscribe EMAIL Remove a single subscriber with the given EMAIL.
10281028
*/
10291029
void alert_cmd(void){
10301030
const char *zCmd;
10311031
--- src/alerts.c
+++ src/alerts.c
@@ -1020,11 +1020,11 @@
1020 ** configuration. Options:
1021 **
1022 ** --body FILENAME
1023 ** --smtp-trace
1024 ** --stdout
1025 ** --subject|-S SUBJECT
1026 **
1027 ** unsubscribe EMAIL Remove a single subscriber with the given EMAIL.
1028 */
1029 void alert_cmd(void){
1030 const char *zCmd;
1031
--- src/alerts.c
+++ src/alerts.c
@@ -1020,11 +1020,11 @@
1020 ** configuration. Options:
1021 **
1022 ** --body FILENAME
1023 ** --smtp-trace
1024 ** --stdout
1025 ** -S|--subject SUBJECT
1026 **
1027 ** unsubscribe EMAIL Remove a single subscriber with the given EMAIL.
1028 */
1029 void alert_cmd(void){
1030 const char *zCmd;
1031
--- src/backoffice.c
+++ src/backoffice.c
@@ -695,11 +695,11 @@
695695
** backoffice has run recently, return immediately.
696696
**
697697
** --nolease Always run backoffice, even if there is a lease
698698
** conflict. This option implies --nodelay. This
699699
** option is added to secondary backoffice commands
700
-** that are invoked by the --poll option.
700
+** that are invoked by the --poll option.
701701
*/
702702
void backoffice_command(void){
703703
int nPoll;
704704
int nMin;
705705
const char *zPoll;
706706
--- src/backoffice.c
+++ src/backoffice.c
@@ -695,11 +695,11 @@
695 ** backoffice has run recently, return immediately.
696 **
697 ** --nolease Always run backoffice, even if there is a lease
698 ** conflict. This option implies --nodelay. This
699 ** option is added to secondary backoffice commands
700 ** that are invoked by the --poll option.
701 */
702 void backoffice_command(void){
703 int nPoll;
704 int nMin;
705 const char *zPoll;
706
--- src/backoffice.c
+++ src/backoffice.c
@@ -695,11 +695,11 @@
695 ** backoffice has run recently, return immediately.
696 **
697 ** --nolease Always run backoffice, even if there is a lease
698 ** conflict. This option implies --nodelay. This
699 ** option is added to secondary backoffice commands
700 ** that are invoked by the --poll option.
701 */
702 void backoffice_command(void){
703 int nPoll;
704 int nMin;
705 const char *zPoll;
706
-3
--- src/blob.c
+++ src/blob.c
@@ -63,11 +63,10 @@
6363
/*
6464
** Seek whence parameter values
6565
*/
6666
#define BLOB_SEEK_SET 1
6767
#define BLOB_SEEK_CUR 2
68
-#define BLOB_SEEK_END 3
6968
7069
#endif /* INTERFACE */
7170
7271
/*
7372
** Make sure a blob is initialized
@@ -579,12 +578,10 @@
579578
int blob_seek(Blob *p, int offset, int whence){
580579
if( whence==BLOB_SEEK_SET ){
581580
p->iCursor = offset;
582581
}else if( whence==BLOB_SEEK_CUR ){
583582
p->iCursor += offset;
584
- }else if( whence==BLOB_SEEK_END ){
585
- p->iCursor = p->nUsed + offset - 1;
586583
}
587584
if( p->iCursor>p->nUsed ){
588585
p->iCursor = p->nUsed;
589586
}
590587
return p->iCursor;
591588
--- src/blob.c
+++ src/blob.c
@@ -63,11 +63,10 @@
63 /*
64 ** Seek whence parameter values
65 */
66 #define BLOB_SEEK_SET 1
67 #define BLOB_SEEK_CUR 2
68 #define BLOB_SEEK_END 3
69
70 #endif /* INTERFACE */
71
72 /*
73 ** Make sure a blob is initialized
@@ -579,12 +578,10 @@
579 int blob_seek(Blob *p, int offset, int whence){
580 if( whence==BLOB_SEEK_SET ){
581 p->iCursor = offset;
582 }else if( whence==BLOB_SEEK_CUR ){
583 p->iCursor += offset;
584 }else if( whence==BLOB_SEEK_END ){
585 p->iCursor = p->nUsed + offset - 1;
586 }
587 if( p->iCursor>p->nUsed ){
588 p->iCursor = p->nUsed;
589 }
590 return p->iCursor;
591
--- src/blob.c
+++ src/blob.c
@@ -63,11 +63,10 @@
63 /*
64 ** Seek whence parameter values
65 */
66 #define BLOB_SEEK_SET 1
67 #define BLOB_SEEK_CUR 2
 
68
69 #endif /* INTERFACE */
70
71 /*
72 ** Make sure a blob is initialized
@@ -579,12 +578,10 @@
578 int blob_seek(Blob *p, int offset, int whence){
579 if( whence==BLOB_SEEK_SET ){
580 p->iCursor = offset;
581 }else if( whence==BLOB_SEEK_CUR ){
582 p->iCursor += offset;
 
 
583 }
584 if( p->iCursor>p->nUsed ){
585 p->iCursor = p->nUsed;
586 }
587 return p->iCursor;
588
+1 -2
--- src/branch.c
+++ src/branch.c
@@ -384,11 +384,11 @@
384384
** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
385385
** Either no timezone suffix or "Z" means UTC.
386386
**
387387
** Options valid for all subcommands:
388388
**
389
-** -R|--repository FILE Run commands on repository FILE
389
+** -R|--repository REPO Run commands on repository REPO
390390
*/
391391
void branch_cmd(void){
392392
int n;
393393
const char *zCmd = "list";
394394
db_find_and_open_repository(0, 0);
@@ -683,11 +683,10 @@
683683
style_set_current_feature("branch");
684684
style_header("Branches");
685685
style_submenu_element("List", "brlist");
686686
login_anonymous_available();
687687
timeline_ss_submenu();
688
- cookie_render();
689688
@ <h2>The initial check-in for each branch:</h2>
690689
blob_append(&sql, timeline_query_for_www(), -1);
691690
blob_append_sql(&sql,
692691
"AND blob.rid IN (SELECT rid FROM tagxref"
693692
" WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH);
694693
--- src/branch.c
+++ src/branch.c
@@ -384,11 +384,11 @@
384 ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
385 ** Either no timezone suffix or "Z" means UTC.
386 **
387 ** Options valid for all subcommands:
388 **
389 ** -R|--repository FILE Run commands on repository FILE
390 */
391 void branch_cmd(void){
392 int n;
393 const char *zCmd = "list";
394 db_find_and_open_repository(0, 0);
@@ -683,11 +683,10 @@
683 style_set_current_feature("branch");
684 style_header("Branches");
685 style_submenu_element("List", "brlist");
686 login_anonymous_available();
687 timeline_ss_submenu();
688 cookie_render();
689 @ <h2>The initial check-in for each branch:</h2>
690 blob_append(&sql, timeline_query_for_www(), -1);
691 blob_append_sql(&sql,
692 "AND blob.rid IN (SELECT rid FROM tagxref"
693 " WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH);
694
--- src/branch.c
+++ src/branch.c
@@ -384,11 +384,11 @@
384 ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
385 ** Either no timezone suffix or "Z" means UTC.
386 **
387 ** Options valid for all subcommands:
388 **
389 ** -R|--repository REPO Run commands on repository REPO
390 */
391 void branch_cmd(void){
392 int n;
393 const char *zCmd = "list";
394 db_find_and_open_repository(0, 0);
@@ -683,11 +683,10 @@
683 style_set_current_feature("branch");
684 style_header("Branches");
685 style_submenu_element("List", "brlist");
686 login_anonymous_available();
687 timeline_ss_submenu();
 
688 @ <h2>The initial check-in for each branch:</h2>
689 blob_append(&sql, timeline_query_for_www(), -1);
690 blob_append_sql(&sql,
691 "AND blob.rid IN (SELECT rid FROM tagxref"
692 " WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH);
693
+25 -4
--- src/browse.c
+++ src/browse.c
@@ -127,12 +127,13 @@
127127
** query parameter is present. If ci= is missing, the union of files
128128
** across all check-ins is shown.
129129
**
130130
** Query parameters:
131131
**
132
+** ci=LABEL Show only files in this check-in. Optional.
132133
** name=PATH Directory to display. Optional. Top-level if missing
133
-** ci=LABEL Show only files in this check-in. Optional.
134
+** re=REGEXP Show only files matching REGEXP
134135
** type=TYPE TYPE=flat: use this display
135136
** TYPE=tree: use the /tree display instead
136137
** noreadme Do not attempt to display the README file.
137138
*/
138139
void page_dir(void){
@@ -150,10 +151,12 @@
150151
int linkTip = 1;
151152
HQuery sURI;
152153
int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
153154
int isBranchCI = 0; /* True if ci= refers to a branch name */
154155
char *zHeader = 0;
156
+ const char *zRegexp; /* The re= query parameter */
157
+ char *zMatch; /* Extra title text describing the match */
155158
156159
if( zCI && strlen(zCI)==0 ){ zCI = 0; }
157160
if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
158161
login_check_credentials();
159162
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -192,10 +195,17 @@
192195
if( zCI ){
193196
zHeader = mprintf("Files in %s/ of %s", zD, zCI);
194197
}else{
195198
zHeader = mprintf("All File in %s/", zD);
196199
}
200
+ }
201
+ zRegexp = P("re");
202
+ if( zRegexp ){
203
+ zHeader = mprintf("%z matching \"%s\"", zHeader, zRegexp);
204
+ zMatch = mprintf(" matching \"%h\"", zRegexp);
205
+ }else{
206
+ zMatch = "";
197207
}
198208
style_header("%s", zHeader);
199209
fossil_free(zHeader);
200210
style_adunit_config(ADUNIT_RIGHT_OK);
201211
sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
@@ -217,16 +227,19 @@
217227
@ <h2>Files in the top-level directory \
218228
zPrefix = "";
219229
}
220230
if( zCI ){
221231
if( fossil_strcmp(zCI,"tip")==0 ){
222
- @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2>
232
+ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a>\
233
+ @ %s(zMatch)</h2>
223234
}else if( isBranchCI ){
224235
@ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
225
- @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
236
+ @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>\
237
+ @ %s(zMatch)</h2>
226238
}else {
227
- @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
239
+ @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a>\
240
+ @ %s(zMatch)</h2>
228241
}
229242
zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
230243
if( nD==0 ){
231244
style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
232245
}
@@ -302,10 +315,18 @@
302315
db_multi_exec(
303316
"INSERT OR IGNORE INTO localfiles"
304317
" SELECT pathelement(name,0), NULL FROM filename"
305318
);
306319
}
320
+
321
+ /* If the re=REGEXP query parameter is present, filter out names that
322
+ ** do not match the pattern */
323
+ if( zRegexp ){
324
+ db_multi_exec(
325
+ "DELETE FROM localfiles WHERE x NOT REGEXP %Q", zRegexp
326
+ );
327
+ }
307328
308329
/* Generate a multi-column table listing the contents of zD[]
309330
** directory.
310331
*/
311332
mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
312333
--- src/browse.c
+++ src/browse.c
@@ -127,12 +127,13 @@
127 ** query parameter is present. If ci= is missing, the union of files
128 ** across all check-ins is shown.
129 **
130 ** Query parameters:
131 **
 
132 ** name=PATH Directory to display. Optional. Top-level if missing
133 ** ci=LABEL Show only files in this check-in. Optional.
134 ** type=TYPE TYPE=flat: use this display
135 ** TYPE=tree: use the /tree display instead
136 ** noreadme Do not attempt to display the README file.
137 */
138 void page_dir(void){
@@ -150,10 +151,12 @@
150 int linkTip = 1;
151 HQuery sURI;
152 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
153 int isBranchCI = 0; /* True if ci= refers to a branch name */
154 char *zHeader = 0;
 
 
155
156 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
157 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
158 login_check_credentials();
159 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -192,10 +195,17 @@
192 if( zCI ){
193 zHeader = mprintf("Files in %s/ of %s", zD, zCI);
194 }else{
195 zHeader = mprintf("All File in %s/", zD);
196 }
 
 
 
 
 
 
 
197 }
198 style_header("%s", zHeader);
199 fossil_free(zHeader);
200 style_adunit_config(ADUNIT_RIGHT_OK);
201 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
@@ -217,16 +227,19 @@
217 @ <h2>Files in the top-level directory \
218 zPrefix = "";
219 }
220 if( zCI ){
221 if( fossil_strcmp(zCI,"tip")==0 ){
222 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2>
 
223 }else if( isBranchCI ){
224 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
225 @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
 
226 }else {
227 @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
 
228 }
229 zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
230 if( nD==0 ){
231 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
232 }
@@ -302,10 +315,18 @@
302 db_multi_exec(
303 "INSERT OR IGNORE INTO localfiles"
304 " SELECT pathelement(name,0), NULL FROM filename"
305 );
306 }
 
 
 
 
 
 
 
 
307
308 /* Generate a multi-column table listing the contents of zD[]
309 ** directory.
310 */
311 mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
312
--- src/browse.c
+++ src/browse.c
@@ -127,12 +127,13 @@
127 ** query parameter is present. If ci= is missing, the union of files
128 ** across all check-ins is shown.
129 **
130 ** Query parameters:
131 **
132 ** ci=LABEL Show only files in this check-in. Optional.
133 ** name=PATH Directory to display. Optional. Top-level if missing
134 ** re=REGEXP Show only files matching REGEXP
135 ** type=TYPE TYPE=flat: use this display
136 ** TYPE=tree: use the /tree display instead
137 ** noreadme Do not attempt to display the README file.
138 */
139 void page_dir(void){
@@ -150,10 +151,12 @@
151 int linkTip = 1;
152 HQuery sURI;
153 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
154 int isBranchCI = 0; /* True if ci= refers to a branch name */
155 char *zHeader = 0;
156 const char *zRegexp; /* The re= query parameter */
157 char *zMatch; /* Extra title text describing the match */
158
159 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
160 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
161 login_check_credentials();
162 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -192,10 +195,17 @@
195 if( zCI ){
196 zHeader = mprintf("Files in %s/ of %s", zD, zCI);
197 }else{
198 zHeader = mprintf("All File in %s/", zD);
199 }
200 }
201 zRegexp = P("re");
202 if( zRegexp ){
203 zHeader = mprintf("%z matching \"%s\"", zHeader, zRegexp);
204 zMatch = mprintf(" matching \"%h\"", zRegexp);
205 }else{
206 zMatch = "";
207 }
208 style_header("%s", zHeader);
209 fossil_free(zHeader);
210 style_adunit_config(ADUNIT_RIGHT_OK);
211 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
@@ -217,16 +227,19 @@
227 @ <h2>Files in the top-level directory \
228 zPrefix = "";
229 }
230 if( zCI ){
231 if( fossil_strcmp(zCI,"tip")==0 ){
232 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a>\
233 @ %s(zMatch)</h2>
234 }else if( isBranchCI ){
235 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
236 @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>\
237 @ %s(zMatch)</h2>
238 }else {
239 @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a>\
240 @ %s(zMatch)</h2>
241 }
242 zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
243 if( nD==0 ){
244 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
245 }
@@ -302,10 +315,18 @@
315 db_multi_exec(
316 "INSERT OR IGNORE INTO localfiles"
317 " SELECT pathelement(name,0), NULL FROM filename"
318 );
319 }
320
321 /* If the re=REGEXP query parameter is present, filter out names that
322 ** do not match the pattern */
323 if( zRegexp ){
324 db_multi_exec(
325 "DELETE FROM localfiles WHERE x NOT REGEXP %Q", zRegexp
326 );
327 }
328
329 /* Generate a multi-column table listing the contents of zD[]
330 ** directory.
331 */
332 mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
333
--- src/builtin.c
+++ src/builtin.c
@@ -704,10 +704,11 @@
704704
/* This list ordering isn't strictly important. */
705705
{"confirmer", 0, 0},
706706
{"copybutton", 0, "dom\0"},
707707
{"dom", 0, 0},
708708
{"fetch", 0, 0},
709
+ {"info-diff", 0, "dom\0"},
709710
{"numbered-lines", 0, "popupwidget\0copybutton\0"},
710711
{"pikchr", 0, "dom\0"},
711712
{"popupwidget", 0, "dom\0"},
712713
{"storage", 0, 0},
713714
{"tabs", 0, "dom\0"}
714715
--- src/builtin.c
+++ src/builtin.c
@@ -704,10 +704,11 @@
704 /* This list ordering isn't strictly important. */
705 {"confirmer", 0, 0},
706 {"copybutton", 0, "dom\0"},
707 {"dom", 0, 0},
708 {"fetch", 0, 0},
 
709 {"numbered-lines", 0, "popupwidget\0copybutton\0"},
710 {"pikchr", 0, "dom\0"},
711 {"popupwidget", 0, "dom\0"},
712 {"storage", 0, 0},
713 {"tabs", 0, "dom\0"}
714
--- src/builtin.c
+++ src/builtin.c
@@ -704,10 +704,11 @@
704 /* This list ordering isn't strictly important. */
705 {"confirmer", 0, 0},
706 {"copybutton", 0, "dom\0"},
707 {"dom", 0, 0},
708 {"fetch", 0, 0},
709 {"info-diff", 0, "dom\0"},
710 {"numbered-lines", 0, "popupwidget\0copybutton\0"},
711 {"pikchr", 0, "dom\0"},
712 {"popupwidget", 0, "dom\0"},
713 {"storage", 0, 0},
714 {"tabs", 0, "dom\0"}
715
+2 -2
--- src/bundle.c
+++ src/bundle.c
@@ -740,12 +740,12 @@
740740
** subset of the check-ins in the repository (usually a single branch)
741741
** described by the --branch, --from, --to, and/or --checkin options,
742742
** at least one of which is required. If BUNDLE already exists, the
743743
** specified content is added to the bundle.
744744
**
745
-** --branch BRANCH Package all check-ins on BRANCH.
746
-** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2.
745
+** --branch BRANCH Package all check-ins on BRANCH
746
+** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2
747747
** --checkin TAG Package the single check-in TAG
748748
** --standalone Do no use delta-encoding against
749749
** artifacts not in the bundle
750750
**
751751
** > fossil bundle extend BUNDLE
752752
--- src/bundle.c
+++ src/bundle.c
@@ -740,12 +740,12 @@
740 ** subset of the check-ins in the repository (usually a single branch)
741 ** described by the --branch, --from, --to, and/or --checkin options,
742 ** at least one of which is required. If BUNDLE already exists, the
743 ** specified content is added to the bundle.
744 **
745 ** --branch BRANCH Package all check-ins on BRANCH.
746 ** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2.
747 ** --checkin TAG Package the single check-in TAG
748 ** --standalone Do no use delta-encoding against
749 ** artifacts not in the bundle
750 **
751 ** > fossil bundle extend BUNDLE
752
--- src/bundle.c
+++ src/bundle.c
@@ -740,12 +740,12 @@
740 ** subset of the check-ins in the repository (usually a single branch)
741 ** described by the --branch, --from, --to, and/or --checkin options,
742 ** at least one of which is required. If BUNDLE already exists, the
743 ** specified content is added to the bundle.
744 **
745 ** --branch BRANCH Package all check-ins on BRANCH
746 ** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2
747 ** --checkin TAG Package the single check-in TAG
748 ** --standalone Do no use delta-encoding against
749 ** artifacts not in the bundle
750 **
751 ** > fossil bundle extend BUNDLE
752
+12
--- src/cgi.c
+++ src/cgi.c
@@ -1159,16 +1159,28 @@
11591159
#endif
11601160
z = (char*)P("HTTP_COOKIE");
11611161
if( z ){
11621162
z = fossil_strdup(z);
11631163
add_param_list(z, ';');
1164
+ z = (char*)cookie_value("skin",0);
1165
+ if(z){
1166
+ skin_use_alternative(z, 2);
1167
+ }
11641168
}
11651169
11661170
z = (char*)P("QUERY_STRING");
11671171
if( z ){
11681172
z = fossil_strdup(z);
11691173
add_param_list(z, '&');
1174
+ z = (char*)P("skin");
1175
+ if(z){
1176
+ char *zErr = skin_use_alternative(z, 2);
1177
+ if(!zErr && !P("once")){
1178
+ cookie_write_parameter("skin","skin",z);
1179
+ }
1180
+ fossil_free(zErr);
1181
+ }
11701182
}
11711183
11721184
z = (char*)P("REMOTE_ADDR");
11731185
if( z ){
11741186
g.zIpAddr = fossil_strdup(z);
11751187
--- src/cgi.c
+++ src/cgi.c
@@ -1159,16 +1159,28 @@
1159 #endif
1160 z = (char*)P("HTTP_COOKIE");
1161 if( z ){
1162 z = fossil_strdup(z);
1163 add_param_list(z, ';');
 
 
 
 
1164 }
1165
1166 z = (char*)P("QUERY_STRING");
1167 if( z ){
1168 z = fossil_strdup(z);
1169 add_param_list(z, '&');
 
 
 
 
 
 
 
 
1170 }
1171
1172 z = (char*)P("REMOTE_ADDR");
1173 if( z ){
1174 g.zIpAddr = fossil_strdup(z);
1175
--- src/cgi.c
+++ src/cgi.c
@@ -1159,16 +1159,28 @@
1159 #endif
1160 z = (char*)P("HTTP_COOKIE");
1161 if( z ){
1162 z = fossil_strdup(z);
1163 add_param_list(z, ';');
1164 z = (char*)cookie_value("skin",0);
1165 if(z){
1166 skin_use_alternative(z, 2);
1167 }
1168 }
1169
1170 z = (char*)P("QUERY_STRING");
1171 if( z ){
1172 z = fossil_strdup(z);
1173 add_param_list(z, '&');
1174 z = (char*)P("skin");
1175 if(z){
1176 char *zErr = skin_use_alternative(z, 2);
1177 if(!zErr && !P("once")){
1178 cookie_write_parameter("skin","skin",z);
1179 }
1180 fossil_free(zErr);
1181 }
1182 }
1183
1184 z = (char*)P("REMOTE_ADDR");
1185 if( z ){
1186 g.zIpAddr = fossil_strdup(z);
1187
+13 -13
--- src/checkin.c
+++ src/checkin.c
@@ -677,11 +677,11 @@
677677
** Options:
678678
** --age Show when each file was committed.
679679
** -v|--verbose Provide extra information about each file.
680680
** -t Sort output in time order.
681681
** -r VERSION The specific check-in to list.
682
-** -R|--repository FILE Extract info from repository FILE.
682
+** -R|--repository REPO Extract info from repository REPO.
683683
** --hash With -v, verify file status using hashing
684684
** rather than relying on file sizes and mtimes.
685685
**
686686
** See also: [[changes]], [[extras]], [[status]]
687687
*/
@@ -831,17 +831,17 @@
831831
**
832832
** Pathnames are displayed according to the "relative-paths" setting,
833833
** unless overridden by the --abs-paths or --rel-paths options.
834834
**
835835
** Options:
836
-** --abs-paths Display absolute pathnames.
836
+** --abs-paths Display absolute pathnames
837837
** --case-sensitive BOOL Override case-sensitive setting
838838
** --dotfiles Include files beginning with a dot (".")
839839
** --header Identify the repository if there are extras
840840
** --ignore CSG Ignore files matching patterns from the argument
841841
** --rel-paths Display pathnames relative to the current working
842
-** directory.
842
+** directory
843843
**
844844
** See also: [[changes]], [[clean]], [[status]]
845845
*/
846846
void extras_cmd(void){
847847
Blob report = BLOB_INITIALIZER;
@@ -928,19 +928,19 @@
928928
** be removed. Using this option will automatically
929929
** enable the --emptydirs option as well.
930930
** --disable-undo WARNING: This option disables use of the undo
931931
** mechanism for this clean operation and should be
932932
** used with extreme caution.
933
-** --dotfiles Include files beginning with a dot (".").
933
+** --dotfiles Include files beginning with a dot (".")
934934
** --emptydirs Remove any empty directories that are not
935935
** explicitly exempted via the empty-dirs setting
936936
** or another applicable setting or command line
937937
** argument. Matching files, if any, are removed
938938
** prior to checking for any empty directories;
939939
** therefore, directories that contain only files
940940
** that were removed will be removed as well.
941
-** -f|--force Remove files without prompting.
941
+** -f|--force Remove files without prompting
942942
** -i|--prompt Prompt before removing each file. This option
943943
** implies the --disable-undo option.
944944
** -x|--verily WARNING: Removes everything that is not a managed
945945
** file or the repository itself. This option
946946
** implies the --force, --emptydirs, --dotfiles, and
@@ -951,19 +951,19 @@
951951
** --clean CSG WARNING: Never prompt to delete any files matching
952952
** this comma separated list of glob patterns. Also,
953953
** deletions of any files matching this pattern list
954954
** cannot be undone.
955955
** --ignore CSG Ignore files matching patterns from the
956
-** comma separated list of glob patterns.
956
+** comma separated list of glob patterns
957957
** --keep <CSG> Keep files matching this comma separated
958
-** list of glob patterns.
958
+** list of glob patterns
959959
** -n|--dry-run Delete nothing, but display what would have been
960
-** deleted.
961
-** --no-prompt This option disables prompting the user for input
962
-** and assumes an answer of 'No' for every question.
963
-** --temp Remove only Fossil-generated temporary files.
964
-** -v|--verbose Show all files as they are removed.
960
+** deleted
961
+** --no-prompt Do not prompt the user for input and assume an
962
+** answer of 'No' for every question
963
+** --temp Remove only Fossil-generated temporary files
964
+** -v|--verbose Show all files as they are removed
965965
**
966966
** See also: [[addremove]], [[extras]], [[status]]
967967
*/
968968
void clean_cmd(void){
969969
int allFileFlag, allDirFlag, dryRunFlag, verboseFlag;
@@ -1739,11 +1739,11 @@
17391739
char *zMergeUuid;
17401740
int mid = db_column_int(&q, 0);
17411741
if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ){
17421742
continue;
17431743
}
1744
- zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
1744
+ zMergeUuid = rid_to_uuid(mid);
17451745
if( zMergeUuid ){
17461746
blob_appendf(pOut, " %s", zMergeUuid);
17471747
if( p->verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
17481748
free(zMergeUuid);
17491749
}
17501750
--- src/checkin.c
+++ src/checkin.c
@@ -677,11 +677,11 @@
677 ** Options:
678 ** --age Show when each file was committed.
679 ** -v|--verbose Provide extra information about each file.
680 ** -t Sort output in time order.
681 ** -r VERSION The specific check-in to list.
682 ** -R|--repository FILE Extract info from repository FILE.
683 ** --hash With -v, verify file status using hashing
684 ** rather than relying on file sizes and mtimes.
685 **
686 ** See also: [[changes]], [[extras]], [[status]]
687 */
@@ -831,17 +831,17 @@
831 **
832 ** Pathnames are displayed according to the "relative-paths" setting,
833 ** unless overridden by the --abs-paths or --rel-paths options.
834 **
835 ** Options:
836 ** --abs-paths Display absolute pathnames.
837 ** --case-sensitive BOOL Override case-sensitive setting
838 ** --dotfiles Include files beginning with a dot (".")
839 ** --header Identify the repository if there are extras
840 ** --ignore CSG Ignore files matching patterns from the argument
841 ** --rel-paths Display pathnames relative to the current working
842 ** directory.
843 **
844 ** See also: [[changes]], [[clean]], [[status]]
845 */
846 void extras_cmd(void){
847 Blob report = BLOB_INITIALIZER;
@@ -928,19 +928,19 @@
928 ** be removed. Using this option will automatically
929 ** enable the --emptydirs option as well.
930 ** --disable-undo WARNING: This option disables use of the undo
931 ** mechanism for this clean operation and should be
932 ** used with extreme caution.
933 ** --dotfiles Include files beginning with a dot (".").
934 ** --emptydirs Remove any empty directories that are not
935 ** explicitly exempted via the empty-dirs setting
936 ** or another applicable setting or command line
937 ** argument. Matching files, if any, are removed
938 ** prior to checking for any empty directories;
939 ** therefore, directories that contain only files
940 ** that were removed will be removed as well.
941 ** -f|--force Remove files without prompting.
942 ** -i|--prompt Prompt before removing each file. This option
943 ** implies the --disable-undo option.
944 ** -x|--verily WARNING: Removes everything that is not a managed
945 ** file or the repository itself. This option
946 ** implies the --force, --emptydirs, --dotfiles, and
@@ -951,19 +951,19 @@
951 ** --clean CSG WARNING: Never prompt to delete any files matching
952 ** this comma separated list of glob patterns. Also,
953 ** deletions of any files matching this pattern list
954 ** cannot be undone.
955 ** --ignore CSG Ignore files matching patterns from the
956 ** comma separated list of glob patterns.
957 ** --keep <CSG> Keep files matching this comma separated
958 ** list of glob patterns.
959 ** -n|--dry-run Delete nothing, but display what would have been
960 ** deleted.
961 ** --no-prompt This option disables prompting the user for input
962 ** and assumes an answer of 'No' for every question.
963 ** --temp Remove only Fossil-generated temporary files.
964 ** -v|--verbose Show all files as they are removed.
965 **
966 ** See also: [[addremove]], [[extras]], [[status]]
967 */
968 void clean_cmd(void){
969 int allFileFlag, allDirFlag, dryRunFlag, verboseFlag;
@@ -1739,11 +1739,11 @@
1739 char *zMergeUuid;
1740 int mid = db_column_int(&q, 0);
1741 if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ){
1742 continue;
1743 }
1744 zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
1745 if( zMergeUuid ){
1746 blob_appendf(pOut, " %s", zMergeUuid);
1747 if( p->verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
1748 free(zMergeUuid);
1749 }
1750
--- src/checkin.c
+++ src/checkin.c
@@ -677,11 +677,11 @@
677 ** Options:
678 ** --age Show when each file was committed.
679 ** -v|--verbose Provide extra information about each file.
680 ** -t Sort output in time order.
681 ** -r VERSION The specific check-in to list.
682 ** -R|--repository REPO Extract info from repository REPO.
683 ** --hash With -v, verify file status using hashing
684 ** rather than relying on file sizes and mtimes.
685 **
686 ** See also: [[changes]], [[extras]], [[status]]
687 */
@@ -831,17 +831,17 @@
831 **
832 ** Pathnames are displayed according to the "relative-paths" setting,
833 ** unless overridden by the --abs-paths or --rel-paths options.
834 **
835 ** Options:
836 ** --abs-paths Display absolute pathnames
837 ** --case-sensitive BOOL Override case-sensitive setting
838 ** --dotfiles Include files beginning with a dot (".")
839 ** --header Identify the repository if there are extras
840 ** --ignore CSG Ignore files matching patterns from the argument
841 ** --rel-paths Display pathnames relative to the current working
842 ** directory
843 **
844 ** See also: [[changes]], [[clean]], [[status]]
845 */
846 void extras_cmd(void){
847 Blob report = BLOB_INITIALIZER;
@@ -928,19 +928,19 @@
928 ** be removed. Using this option will automatically
929 ** enable the --emptydirs option as well.
930 ** --disable-undo WARNING: This option disables use of the undo
931 ** mechanism for this clean operation and should be
932 ** used with extreme caution.
933 ** --dotfiles Include files beginning with a dot (".")
934 ** --emptydirs Remove any empty directories that are not
935 ** explicitly exempted via the empty-dirs setting
936 ** or another applicable setting or command line
937 ** argument. Matching files, if any, are removed
938 ** prior to checking for any empty directories;
939 ** therefore, directories that contain only files
940 ** that were removed will be removed as well.
941 ** -f|--force Remove files without prompting
942 ** -i|--prompt Prompt before removing each file. This option
943 ** implies the --disable-undo option.
944 ** -x|--verily WARNING: Removes everything that is not a managed
945 ** file or the repository itself. This option
946 ** implies the --force, --emptydirs, --dotfiles, and
@@ -951,19 +951,19 @@
951 ** --clean CSG WARNING: Never prompt to delete any files matching
952 ** this comma separated list of glob patterns. Also,
953 ** deletions of any files matching this pattern list
954 ** cannot be undone.
955 ** --ignore CSG Ignore files matching patterns from the
956 ** comma separated list of glob patterns
957 ** --keep <CSG> Keep files matching this comma separated
958 ** list of glob patterns
959 ** -n|--dry-run Delete nothing, but display what would have been
960 ** deleted
961 ** --no-prompt Do not prompt the user for input and assume an
962 ** answer of 'No' for every question
963 ** --temp Remove only Fossil-generated temporary files
964 ** -v|--verbose Show all files as they are removed
965 **
966 ** See also: [[addremove]], [[extras]], [[status]]
967 */
968 void clean_cmd(void){
969 int allFileFlag, allDirFlag, dryRunFlag, verboseFlag;
@@ -1739,11 +1739,11 @@
1739 char *zMergeUuid;
1740 int mid = db_column_int(&q, 0);
1741 if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ){
1742 continue;
1743 }
1744 zMergeUuid = rid_to_uuid(mid);
1745 if( zMergeUuid ){
1746 blob_appendf(pOut, " %s", zMergeUuid);
1747 if( p->verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
1748 free(zMergeUuid);
1749 }
1750
+1 -1
--- src/checkout.c
+++ src/checkout.c
@@ -282,11 +282,11 @@
282282
** --force Ignore edited files in the current checkout
283283
** --keep Only update the manifest and manifest.uuid files
284284
** --force-missing Force checkout even if content is missing
285285
** --setmtime Set timestamps of all files to match their SCM-side
286286
** times (the timestamp of the last checkin which modified
287
-** them).
287
+** them)
288288
**
289289
** See also: [[update]]
290290
*/
291291
void checkout_cmd(void){
292292
int forceFlag; /* Force checkout even if edits exist */
293293
--- src/checkout.c
+++ src/checkout.c
@@ -282,11 +282,11 @@
282 ** --force Ignore edited files in the current checkout
283 ** --keep Only update the manifest and manifest.uuid files
284 ** --force-missing Force checkout even if content is missing
285 ** --setmtime Set timestamps of all files to match their SCM-side
286 ** times (the timestamp of the last checkin which modified
287 ** them).
288 **
289 ** See also: [[update]]
290 */
291 void checkout_cmd(void){
292 int forceFlag; /* Force checkout even if edits exist */
293
--- src/checkout.c
+++ src/checkout.c
@@ -282,11 +282,11 @@
282 ** --force Ignore edited files in the current checkout
283 ** --keep Only update the manifest and manifest.uuid files
284 ** --force-missing Force checkout even if content is missing
285 ** --setmtime Set timestamps of all files to match their SCM-side
286 ** times (the timestamp of the last checkin which modified
287 ** them)
288 **
289 ** See also: [[update]]
290 */
291 void checkout_cmd(void){
292 int forceFlag; /* Force checkout even if edits exist */
293
+1 -1
--- src/configure.c
+++ src/configure.c
@@ -769,11 +769,11 @@
769769
**
770770
** Synchronize configuration changes in the local repository with
771771
** the remote repository at URL.
772772
**
773773
** Options:
774
-** -R|--repository FILE Extract info from repository FILE
774
+** -R|--repository REPO Extract info from repository REPO
775775
**
776776
** See also: [[settings]], [[unset]]
777777
*/
778778
void configuration_cmd(void){
779779
int n;
780780
--- src/configure.c
+++ src/configure.c
@@ -769,11 +769,11 @@
769 **
770 ** Synchronize configuration changes in the local repository with
771 ** the remote repository at URL.
772 **
773 ** Options:
774 ** -R|--repository FILE Extract info from repository FILE
775 **
776 ** See also: [[settings]], [[unset]]
777 */
778 void configuration_cmd(void){
779 int n;
780
--- src/configure.c
+++ src/configure.c
@@ -769,11 +769,11 @@
769 **
770 ** Synchronize configuration changes in the local repository with
771 ** the remote repository at URL.
772 **
773 ** Options:
774 ** -R|--repository REPO Extract info from repository REPO
775 **
776 ** See also: [[settings]], [[unset]]
777 */
778 void configuration_cmd(void){
779 int n;
780
+1 -1
--- src/content.c
+++ src/content.c
@@ -324,11 +324,11 @@
324324
** Extract an artifact by its artifact hash and write the results on
325325
** standard output, or if the optional 4th argument is given, in
326326
** the named output file.
327327
**
328328
** Options:
329
-** -R|--repository FILE Extract artifacts from repository FILE
329
+** -R|--repository REPO Extract artifacts from repository REPO
330330
**
331331
** See also: [[finfo]]
332332
*/
333333
void artifact_cmd(void){
334334
int rid;
335335
--- src/content.c
+++ src/content.c
@@ -324,11 +324,11 @@
324 ** Extract an artifact by its artifact hash and write the results on
325 ** standard output, or if the optional 4th argument is given, in
326 ** the named output file.
327 **
328 ** Options:
329 ** -R|--repository FILE Extract artifacts from repository FILE
330 **
331 ** See also: [[finfo]]
332 */
333 void artifact_cmd(void){
334 int rid;
335
--- src/content.c
+++ src/content.c
@@ -324,11 +324,11 @@
324 ** Extract an artifact by its artifact hash and write the results on
325 ** standard output, or if the optional 4th argument is given, in
326 ** the named output file.
327 **
328 ** Options:
329 ** -R|--repository REPO Extract artifacts from repository REPO
330 **
331 ** See also: [[finfo]]
332 */
333 void artifact_cmd(void){
334 int rid;
335
+4 -3
--- src/cookies.c
+++ src/cookies.c
@@ -173,15 +173,16 @@
173173
const char *zDflt /* Default value for the parameter */
174174
){
175175
cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE);
176176
}
177177
178
-/* Update the user preferences cookie, if necessary, and shut down this
179
-** module
178
+/* Update the user preferences cookie, if necessary, and shut down
179
+** this module. The cookie is only emitted if its value has actually
180
+** changed since the request started.
180181
*/
181182
void cookie_render(void){
182
- if( cookies.bChanged && P("udc")!=0 ){
183
+ if( cookies.bChanged ){
183184
Blob new;
184185
int i;
185186
blob_init(&new, 0, 0);
186187
for(i=0;i<cookies.nParam;i++){
187188
if( i>0 ) blob_append(&new, ",", 1);
188189
--- src/cookies.c
+++ src/cookies.c
@@ -173,15 +173,16 @@
173 const char *zDflt /* Default value for the parameter */
174 ){
175 cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE);
176 }
177
178 /* Update the user preferences cookie, if necessary, and shut down this
179 ** module
 
180 */
181 void cookie_render(void){
182 if( cookies.bChanged && P("udc")!=0 ){
183 Blob new;
184 int i;
185 blob_init(&new, 0, 0);
186 for(i=0;i<cookies.nParam;i++){
187 if( i>0 ) blob_append(&new, ",", 1);
188
--- src/cookies.c
+++ src/cookies.c
@@ -173,15 +173,16 @@
173 const char *zDflt /* Default value for the parameter */
174 ){
175 cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE);
176 }
177
178 /* Update the user preferences cookie, if necessary, and shut down
179 ** this module. The cookie is only emitted if its value has actually
180 ** changed since the request started.
181 */
182 void cookie_render(void){
183 if( cookies.bChanged ){
184 Blob new;
185 int i;
186 blob_init(&new, 0, 0);
187 for(i=0;i<cookies.nParam;i++){
188 if( i>0 ) blob_append(&new, ",", 1);
189
+1
--- src/db.c
+++ src/db.c
@@ -4218,10 +4218,11 @@
42184218
** the following default Content Security Policy is used:
42194219
**
42204220
** default-src 'self' data:;
42214221
** script-src 'self' 'nonce-$nonce';
42224222
** style-src 'self' 'unsafe-inline';
4223
+** img-src * data:;
42234224
**
42244225
** The default CSP is recommended. The main reason to change
42254226
** this setting would be to add CDNs from which it is safe to
42264227
** load additional content.
42274228
*/
42284229
--- src/db.c
+++ src/db.c
@@ -4218,10 +4218,11 @@
4218 ** the following default Content Security Policy is used:
4219 **
4220 ** default-src 'self' data:;
4221 ** script-src 'self' 'nonce-$nonce';
4222 ** style-src 'self' 'unsafe-inline';
 
4223 **
4224 ** The default CSP is recommended. The main reason to change
4225 ** this setting would be to add CDNs from which it is safe to
4226 ** load additional content.
4227 */
4228
--- src/db.c
+++ src/db.c
@@ -4218,10 +4218,11 @@
4218 ** the following default Content Security Policy is used:
4219 **
4220 ** default-src 'self' data:;
4221 ** script-src 'self' 'nonce-$nonce';
4222 ** style-src 'self' 'unsafe-inline';
4223 ** img-src * data:;
4224 **
4225 ** The default CSP is recommended. The main reason to change
4226 ** this setting would be to add CDNs from which it is safe to
4227 ** load additional content.
4228 */
4229
--- src/default.css
+++ src/default.css
@@ -1763,10 +1763,13 @@
17631763
body.chat #chat-drop-details img {
17641764
max-width: 45%;
17651765
max-height: 45%;
17661766
}
17671767
1768
+input[type="checkbox"].diff-toggle {
1769
+ float: right;
1770
+}
17681771
/* Objects in the "desktoponly" class are invisible on mobile */
17691772
@media screen and (max-width: 600px) {
17701773
.desktoponly {
17711774
display: none;
17721775
}
17731776
--- src/default.css
+++ src/default.css
@@ -1763,10 +1763,13 @@
1763 body.chat #chat-drop-details img {
1764 max-width: 45%;
1765 max-height: 45%;
1766 }
1767
 
 
 
1768 /* Objects in the "desktoponly" class are invisible on mobile */
1769 @media screen and (max-width: 600px) {
1770 .desktoponly {
1771 display: none;
1772 }
1773
--- src/default.css
+++ src/default.css
@@ -1763,10 +1763,13 @@
1763 body.chat #chat-drop-details img {
1764 max-width: 45%;
1765 max-height: 45%;
1766 }
1767
1768 input[type="checkbox"].diff-toggle {
1769 float: right;
1770 }
1771 /* Objects in the "desktoponly" class are invisible on mobile */
1772 @media screen and (max-width: 600px) {
1773 .desktoponly {
1774 display: none;
1775 }
1776
--- src/descendants.c
+++ src/descendants.c
@@ -343,11 +343,11 @@
343343
**
344344
** Find all leaf descendants of the check-in specified or if the argument
345345
** is omitted, of the check-in currently checked out.
346346
**
347347
** Options:
348
-** -R|--repository FILE Extract info from repository FILE
348
+** -R|--repository REPO Extract info from repository REPO
349349
** -W|--width N Width of lines (default is to auto-detect).
350350
** Must be greater than 20 or else 0 for no
351351
** limit, resulting in a one line per entry.
352352
**
353353
** See also: [[finfo]], [[info]], [[leaves]]
@@ -399,15 +399,15 @@
399399
**
400400
** The --recompute flag causes the content of the "leaf" table in the
401401
** repository database to be recomputed.
402402
**
403403
** Options:
404
-** -a|--all show ALL leaves
405
-** --bybranch order output by branch name
406
-** -c|--closed show only closed leaves
407
-** -m|--multiple show only cases with multiple leaves on a single branch
408
-** --recompute recompute the "leaf" table in the repository DB
404
+** -a|--all Show ALL leaves
405
+** --bybranch Order output by branch name
406
+** -c|--closed Show only closed leaves
407
+** -m|--multiple Show only cases with multiple leaves on a single branch
408
+** --recompute Recompute the "leaf" table in the repository DB
409409
** -W|--width N Width of lines (default is to auto-detect). Must be
410410
** more than 39 or else 0 no limit, resulting in a single
411411
** line per entry.
412412
**
413413
** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
@@ -422,10 +422,11 @@
422422
int multipleFlag = find_option("multiple","m",0)!=0;
423423
const char *zWidth = find_option("width","W",1);
424424
char *zLastBr = 0;
425425
int n, width;
426426
char zLineNo[10];
427
+ char * const zMainBranch = db_get("main-branch","trunk");
427428
428429
if( multipleFlag ) byBranch = 1;
429430
if( zWidth ){
430431
width = atoi(zWidth);
431432
if( (width!=0) && (width<=39) ){
@@ -491,11 +492,12 @@
491492
while( db_step(&q)==SQLITE_ROW ){
492493
const char *zId = db_column_text(&q, 1);
493494
const char *zDate = db_column_text(&q, 2);
494495
const char *zCom = db_column_text(&q, 3);
495496
const char *zBr = db_column_text(&q, 7);
496
- char *z;
497
+ char *z = 0;
498
+ char * zBranchPoint = 0;
497499
498500
if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
499501
fossil_print("*** %s ***\n", zBr);
500502
fossil_free(zLastBr);
501503
zLastBr = fossil_strdup(zBr);
@@ -502,14 +504,27 @@
502504
if( multipleFlag ) n = 0;
503505
}
504506
n++;
505507
sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
506508
fossil_print("%6s ", zLineNo);
507
- z = mprintf("%s [%S] %s", zDate, zId, zCom);
509
+ if(0!=fossil_strcmp(zBr,zMainBranch)){
510
+ int ridOfRoot;
511
+ z = mprintf("root:%s", zId);
512
+ ridOfRoot = symbolic_name_to_rid(z, "ci");
513
+ if(ridOfRoot>0){
514
+ zBranchPoint = mprintf(" (branched from: [%.*z])", hash_digits(0),
515
+ rid_to_uuid(ridOfRoot));
516
+ }
517
+ fossil_free(z);
518
+ }
519
+ z = mprintf("%s [%S] %s%s", zDate, zId, zCom,
520
+ zBranchPoint ? zBranchPoint : "");
508521
comment_print(z, zCom, 7, width, get_comment_format());
509522
fossil_free(z);
523
+ fossil_free(zBranchPoint);
510524
}
525
+ fossil_free(zMainBranch);
511526
fossil_free(zLastBr);
512527
db_finalize(&q);
513528
}
514529
515530
/*
@@ -565,11 +580,10 @@
565580
url_reset(&url);
566581
style_set_current_feature("leaves");
567582
style_header("Leaves");
568583
login_anonymous_available();
569584
timeline_ss_submenu();
570
- cookie_render();
571585
#if 0
572586
style_sidebox_begin("Nomenclature:", "33%");
573587
@ <ol>
574588
@ <li> A <div class="sideboxDescribed">leaf</div>
575589
@ is a check-in with no descendants in the same branch.</li>
576590
--- src/descendants.c
+++ src/descendants.c
@@ -343,11 +343,11 @@
343 **
344 ** Find all leaf descendants of the check-in specified or if the argument
345 ** is omitted, of the check-in currently checked out.
346 **
347 ** Options:
348 ** -R|--repository FILE Extract info from repository FILE
349 ** -W|--width N Width of lines (default is to auto-detect).
350 ** Must be greater than 20 or else 0 for no
351 ** limit, resulting in a one line per entry.
352 **
353 ** See also: [[finfo]], [[info]], [[leaves]]
@@ -399,15 +399,15 @@
399 **
400 ** The --recompute flag causes the content of the "leaf" table in the
401 ** repository database to be recomputed.
402 **
403 ** Options:
404 ** -a|--all show ALL leaves
405 ** --bybranch order output by branch name
406 ** -c|--closed show only closed leaves
407 ** -m|--multiple show only cases with multiple leaves on a single branch
408 ** --recompute recompute the "leaf" table in the repository DB
409 ** -W|--width N Width of lines (default is to auto-detect). Must be
410 ** more than 39 or else 0 no limit, resulting in a single
411 ** line per entry.
412 **
413 ** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
@@ -422,10 +422,11 @@
422 int multipleFlag = find_option("multiple","m",0)!=0;
423 const char *zWidth = find_option("width","W",1);
424 char *zLastBr = 0;
425 int n, width;
426 char zLineNo[10];
 
427
428 if( multipleFlag ) byBranch = 1;
429 if( zWidth ){
430 width = atoi(zWidth);
431 if( (width!=0) && (width<=39) ){
@@ -491,11 +492,12 @@
491 while( db_step(&q)==SQLITE_ROW ){
492 const char *zId = db_column_text(&q, 1);
493 const char *zDate = db_column_text(&q, 2);
494 const char *zCom = db_column_text(&q, 3);
495 const char *zBr = db_column_text(&q, 7);
496 char *z;
 
497
498 if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
499 fossil_print("*** %s ***\n", zBr);
500 fossil_free(zLastBr);
501 zLastBr = fossil_strdup(zBr);
@@ -502,14 +504,27 @@
502 if( multipleFlag ) n = 0;
503 }
504 n++;
505 sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
506 fossil_print("%6s ", zLineNo);
507 z = mprintf("%s [%S] %s", zDate, zId, zCom);
 
 
 
 
 
 
 
 
 
 
 
508 comment_print(z, zCom, 7, width, get_comment_format());
509 fossil_free(z);
 
510 }
 
511 fossil_free(zLastBr);
512 db_finalize(&q);
513 }
514
515 /*
@@ -565,11 +580,10 @@
565 url_reset(&url);
566 style_set_current_feature("leaves");
567 style_header("Leaves");
568 login_anonymous_available();
569 timeline_ss_submenu();
570 cookie_render();
571 #if 0
572 style_sidebox_begin("Nomenclature:", "33%");
573 @ <ol>
574 @ <li> A <div class="sideboxDescribed">leaf</div>
575 @ is a check-in with no descendants in the same branch.</li>
576
--- src/descendants.c
+++ src/descendants.c
@@ -343,11 +343,11 @@
343 **
344 ** Find all leaf descendants of the check-in specified or if the argument
345 ** is omitted, of the check-in currently checked out.
346 **
347 ** Options:
348 ** -R|--repository REPO Extract info from repository REPO
349 ** -W|--width N Width of lines (default is to auto-detect).
350 ** Must be greater than 20 or else 0 for no
351 ** limit, resulting in a one line per entry.
352 **
353 ** See also: [[finfo]], [[info]], [[leaves]]
@@ -399,15 +399,15 @@
399 **
400 ** The --recompute flag causes the content of the "leaf" table in the
401 ** repository database to be recomputed.
402 **
403 ** Options:
404 ** -a|--all Show ALL leaves
405 ** --bybranch Order output by branch name
406 ** -c|--closed Show only closed leaves
407 ** -m|--multiple Show only cases with multiple leaves on a single branch
408 ** --recompute Recompute the "leaf" table in the repository DB
409 ** -W|--width N Width of lines (default is to auto-detect). Must be
410 ** more than 39 or else 0 no limit, resulting in a single
411 ** line per entry.
412 **
413 ** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
@@ -422,10 +422,11 @@
422 int multipleFlag = find_option("multiple","m",0)!=0;
423 const char *zWidth = find_option("width","W",1);
424 char *zLastBr = 0;
425 int n, width;
426 char zLineNo[10];
427 char * const zMainBranch = db_get("main-branch","trunk");
428
429 if( multipleFlag ) byBranch = 1;
430 if( zWidth ){
431 width = atoi(zWidth);
432 if( (width!=0) && (width<=39) ){
@@ -491,11 +492,12 @@
492 while( db_step(&q)==SQLITE_ROW ){
493 const char *zId = db_column_text(&q, 1);
494 const char *zDate = db_column_text(&q, 2);
495 const char *zCom = db_column_text(&q, 3);
496 const char *zBr = db_column_text(&q, 7);
497 char *z = 0;
498 char * zBranchPoint = 0;
499
500 if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
501 fossil_print("*** %s ***\n", zBr);
502 fossil_free(zLastBr);
503 zLastBr = fossil_strdup(zBr);
@@ -502,14 +504,27 @@
504 if( multipleFlag ) n = 0;
505 }
506 n++;
507 sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
508 fossil_print("%6s ", zLineNo);
509 if(0!=fossil_strcmp(zBr,zMainBranch)){
510 int ridOfRoot;
511 z = mprintf("root:%s", zId);
512 ridOfRoot = symbolic_name_to_rid(z, "ci");
513 if(ridOfRoot>0){
514 zBranchPoint = mprintf(" (branched from: [%.*z])", hash_digits(0),
515 rid_to_uuid(ridOfRoot));
516 }
517 fossil_free(z);
518 }
519 z = mprintf("%s [%S] %s%s", zDate, zId, zCom,
520 zBranchPoint ? zBranchPoint : "");
521 comment_print(z, zCom, 7, width, get_comment_format());
522 fossil_free(z);
523 fossil_free(zBranchPoint);
524 }
525 fossil_free(zMainBranch);
526 fossil_free(zLastBr);
527 db_finalize(&q);
528 }
529
530 /*
@@ -565,11 +580,10 @@
580 url_reset(&url);
581 style_set_current_feature("leaves");
582 style_header("Leaves");
583 login_anonymous_available();
584 timeline_ss_submenu();
 
585 #if 0
586 style_sidebox_begin("Nomenclature:", "33%");
587 @ <ol>
588 @ <li> A <div class="sideboxDescribed">leaf</div>
589 @ is a check-in with no descendants in the same branch.</li>
590
+1 -1
--- src/dispatch.c
+++ src/dispatch.c
@@ -1010,11 +1010,11 @@
10101010
@ --sqlstats Show SQL usage statistics when done
10111011
@ --sqltrace Trace all SQL commands
10121012
@ --sshtrace Trace SSH activity
10131013
@ --ssl-identity NAME Set the SSL identity to NAME
10141014
@ --systemtrace Trace calls to system()
1015
-@ --user|-U USER Make the default user be USER
1015
+@ -U|--user USER Make the default user be USER
10161016
@ --utc Display times using UTC
10171017
@ --vfs NAME Cause SQLite to use the NAME VFS
10181018
;
10191019
10201020
/*
10211021
--- src/dispatch.c
+++ src/dispatch.c
@@ -1010,11 +1010,11 @@
1010 @ --sqlstats Show SQL usage statistics when done
1011 @ --sqltrace Trace all SQL commands
1012 @ --sshtrace Trace SSH activity
1013 @ --ssl-identity NAME Set the SSL identity to NAME
1014 @ --systemtrace Trace calls to system()
1015 @ --user|-U USER Make the default user be USER
1016 @ --utc Display times using UTC
1017 @ --vfs NAME Cause SQLite to use the NAME VFS
1018 ;
1019
1020 /*
1021
--- src/dispatch.c
+++ src/dispatch.c
@@ -1010,11 +1010,11 @@
1010 @ --sqlstats Show SQL usage statistics when done
1011 @ --sqltrace Trace all SQL commands
1012 @ --sshtrace Trace SSH activity
1013 @ --ssl-identity NAME Set the SSL identity to NAME
1014 @ --systemtrace Trace calls to system()
1015 @ -U|--user USER Make the default user be USER
1016 @ --utc Display times using UTC
1017 @ --vfs NAME Cause SQLite to use the NAME VFS
1018 ;
1019
1020 /*
1021
+2 -2
--- src/doc.c
+++ src/doc.c
@@ -73,11 +73,11 @@
7373
return "unknown/unknown";
7474
}
7575
7676
/* A table of mimetypes based on file suffixes.
7777
** Suffixes must be in sorted order so that we can do a binary
78
-** search to find the mime-type
78
+** search to find the mimetype.
7979
*/
8080
static const struct {
8181
const char *zSuffix; /* The file suffix */
8282
int size; /* Length of the suffix */
8383
const char *zMimetype; /* The corresponding mimetype */
@@ -427,11 +427,11 @@
427427
style_script_end();
428428
}
429429
}
430430
431431
/*
432
-** Guess the mime-type of a document based on its name.
432
+** Guess the mimetype of a document based on its name.
433433
*/
434434
const char *mimetype_from_name(const char *zName){
435435
const char *z;
436436
int i;
437437
int first, last;
438438
--- src/doc.c
+++ src/doc.c
@@ -73,11 +73,11 @@
73 return "unknown/unknown";
74 }
75
76 /* A table of mimetypes based on file suffixes.
77 ** Suffixes must be in sorted order so that we can do a binary
78 ** search to find the mime-type
79 */
80 static const struct {
81 const char *zSuffix; /* The file suffix */
82 int size; /* Length of the suffix */
83 const char *zMimetype; /* The corresponding mimetype */
@@ -427,11 +427,11 @@
427 style_script_end();
428 }
429 }
430
431 /*
432 ** Guess the mime-type of a document based on its name.
433 */
434 const char *mimetype_from_name(const char *zName){
435 const char *z;
436 int i;
437 int first, last;
438
--- src/doc.c
+++ src/doc.c
@@ -73,11 +73,11 @@
73 return "unknown/unknown";
74 }
75
76 /* A table of mimetypes based on file suffixes.
77 ** Suffixes must be in sorted order so that we can do a binary
78 ** search to find the mimetype.
79 */
80 static const struct {
81 const char *zSuffix; /* The file suffix */
82 int size; /* Length of the suffix */
83 const char *zMimetype; /* The corresponding mimetype */
@@ -427,11 +427,11 @@
427 style_script_end();
428 }
429 }
430
431 /*
432 ** Guess the mimetype of a document based on its name.
433 */
434 const char *mimetype_from_name(const char *zName){
435 const char *z;
436 int i;
437 int first, last;
438
+4 -4
--- src/export.c
+++ src/export.c
@@ -478,14 +478,14 @@
478478
**
479479
** If the "--export-marks FILE" option is used, the rid of all commits and
480480
** blobs written on exit for use with "--import-marks" on the next run.
481481
**
482482
** Options:
483
-** --export-marks FILE export rids of exported data to FILE
484
-** --import-marks FILE read rids of data to ignore from FILE
485
-** --rename-trunk NAME use NAME as name of exported trunk branch
486
-** -R|--repository REPOSITORY export the given REPOSITORY
483
+** --export-marks FILE Export rids of exported data to FILE
484
+** --import-marks FILE Read rids of data to ignore from FILE
485
+** --rename-trunk NAME Use NAME as name of exported trunk branch
486
+** -R|--repository REPO Export the given REPOSITORY
487487
**
488488
** See also: import
489489
*/
490490
/*
491491
** COMMAND: export*
492492
--- src/export.c
+++ src/export.c
@@ -478,14 +478,14 @@
478 **
479 ** If the "--export-marks FILE" option is used, the rid of all commits and
480 ** blobs written on exit for use with "--import-marks" on the next run.
481 **
482 ** Options:
483 ** --export-marks FILE export rids of exported data to FILE
484 ** --import-marks FILE read rids of data to ignore from FILE
485 ** --rename-trunk NAME use NAME as name of exported trunk branch
486 ** -R|--repository REPOSITORY export the given REPOSITORY
487 **
488 ** See also: import
489 */
490 /*
491 ** COMMAND: export*
492
--- src/export.c
+++ src/export.c
@@ -478,14 +478,14 @@
478 **
479 ** If the "--export-marks FILE" option is used, the rid of all commits and
480 ** blobs written on exit for use with "--import-marks" on the next run.
481 **
482 ** Options:
483 ** --export-marks FILE Export rids of exported data to FILE
484 ** --import-marks FILE Read rids of data to ignore from FILE
485 ** --rename-trunk NAME Use NAME as name of exported trunk branch
486 ** -R|--repository REPO Export the given REPOSITORY
487 **
488 ** See also: import
489 */
490 /*
491 ** COMMAND: export*
492
+1 -1
--- src/fileedit.c
+++ src/fileedit.c
@@ -753,11 +753,11 @@
753753
int newRid = 0; /* RID of new version */
754754
const char * zFilename; /* argv[2] */
755755
const char * zComment; /* -m comment */
756756
const char * zCommentFile; /* -M FILE */
757757
const char * zAsFilename; /* --as filename */
758
- const char * zRevision; /* --revision|-r [=trunk|checkout] */
758
+ const char * zRevision; /* -r|--revision [=trunk|checkout] */
759759
const char * zUser; /* --user-override */
760760
const char * zDate; /* --date-override */
761761
char const * zManifestFile = 0;/* --save-manifest FILE */
762762
763763
/* This function should perform only the minimal "business logic" it
764764
--- src/fileedit.c
+++ src/fileedit.c
@@ -753,11 +753,11 @@
753 int newRid = 0; /* RID of new version */
754 const char * zFilename; /* argv[2] */
755 const char * zComment; /* -m comment */
756 const char * zCommentFile; /* -M FILE */
757 const char * zAsFilename; /* --as filename */
758 const char * zRevision; /* --revision|-r [=trunk|checkout] */
759 const char * zUser; /* --user-override */
760 const char * zDate; /* --date-override */
761 char const * zManifestFile = 0;/* --save-manifest FILE */
762
763 /* This function should perform only the minimal "business logic" it
764
--- src/fileedit.c
+++ src/fileedit.c
@@ -753,11 +753,11 @@
753 int newRid = 0; /* RID of new version */
754 const char * zFilename; /* argv[2] */
755 const char * zComment; /* -m comment */
756 const char * zCommentFile; /* -M FILE */
757 const char * zAsFilename; /* --as filename */
758 const char * zRevision; /* -r|--revision [=trunk|checkout] */
759 const char * zUser; /* --user-override */
760 const char * zDate; /* --date-override */
761 char const * zManifestFile = 0;/* --save-manifest FILE */
762
763 /* This function should perform only the minimal "business logic" it
764
+7 -8
--- src/finfo.c
+++ src/finfo.c
@@ -39,21 +39,21 @@
3939
** In the -p mode, there's an optional flag "-r|--revision REVISION".
4040
** The specified version (or the latest checked out version) is printed
4141
** to stdout. The -p mode is another form of the "cat" command.
4242
**
4343
** Options:
44
-** -b|--brief display a brief (one line / revision) summary
44
+** -b|--brief Display a brief (one line / revision) summary
4545
** --case-sensitive B Enable or disable case-sensitive filenames. B is a
4646
** boolean: "yes", "no", "true", "false", etc.
47
-** -l|--log select log mode (the default)
47
+** -l|--log Select log mode (the default)
4848
** -n|--limit N Display the first N changes (default unlimited).
4949
** N less than 0 means no limit.
50
-** --offset P skip P changes
51
-** -p|--print select print mode
52
-** -r|--revision R print the given revision (or ckout, if none is given)
50
+** --offset P Skip P changes
51
+** -p|--print Select print mode
52
+** -r|--revision R Print the given revision (or ckout, if none is given)
5353
** to stdout (only in print mode)
54
-** -s|--status select status mode (print a status indicator for FILE)
54
+** -s|--status Select status mode (print a status indicator for FILE)
5555
** -W|--width N Width of lines (default is to auto-detect). Must be
5656
** more than 22 or else 0 to indicate no limit.
5757
**
5858
** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
5959
*/
@@ -241,11 +241,11 @@
241241
** Print on standard output the content of one or more files as they exist
242242
** in the repository. The version currently checked out is shown by default.
243243
** Other versions may be specified using the -r option.
244244
**
245245
** Options:
246
-** -R|--repository FILE Extract artifacts from repository FILE
246
+** -R|--repository REPO Extract artifacts from repository REPO
247247
** -r VERSION The specific check-in containing the file
248248
**
249249
** See also: [[finfo]]
250250
*/
251251
void cat_cmd(void){
@@ -364,11 +364,10 @@
364364
url_initialize(&url, "finfo");
365365
if( brBg ) url_add_parameter(&url, "brbg", 0);
366366
if( uBg ) url_add_parameter(&url, "ubg", 0);
367367
ridFrom = name_to_rid_www("from");
368368
zPrevDate[0] = 0;
369
- cookie_render();
370369
if( fnid==0 ){
371370
@ No such file: %h(zFilename)
372371
style_finish_page();
373372
return;
374373
}
375374
376375
ADDED src/fossil.info-diff.js
--- src/finfo.c
+++ src/finfo.c
@@ -39,21 +39,21 @@
39 ** In the -p mode, there's an optional flag "-r|--revision REVISION".
40 ** The specified version (or the latest checked out version) is printed
41 ** to stdout. The -p mode is another form of the "cat" command.
42 **
43 ** Options:
44 ** -b|--brief display a brief (one line / revision) summary
45 ** --case-sensitive B Enable or disable case-sensitive filenames. B is a
46 ** boolean: "yes", "no", "true", "false", etc.
47 ** -l|--log select log mode (the default)
48 ** -n|--limit N Display the first N changes (default unlimited).
49 ** N less than 0 means no limit.
50 ** --offset P skip P changes
51 ** -p|--print select print mode
52 ** -r|--revision R print the given revision (or ckout, if none is given)
53 ** to stdout (only in print mode)
54 ** -s|--status select status mode (print a status indicator for FILE)
55 ** -W|--width N Width of lines (default is to auto-detect). Must be
56 ** more than 22 or else 0 to indicate no limit.
57 **
58 ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
59 */
@@ -241,11 +241,11 @@
241 ** Print on standard output the content of one or more files as they exist
242 ** in the repository. The version currently checked out is shown by default.
243 ** Other versions may be specified using the -r option.
244 **
245 ** Options:
246 ** -R|--repository FILE Extract artifacts from repository FILE
247 ** -r VERSION The specific check-in containing the file
248 **
249 ** See also: [[finfo]]
250 */
251 void cat_cmd(void){
@@ -364,11 +364,10 @@
364 url_initialize(&url, "finfo");
365 if( brBg ) url_add_parameter(&url, "brbg", 0);
366 if( uBg ) url_add_parameter(&url, "ubg", 0);
367 ridFrom = name_to_rid_www("from");
368 zPrevDate[0] = 0;
369 cookie_render();
370 if( fnid==0 ){
371 @ No such file: %h(zFilename)
372 style_finish_page();
373 return;
374 }
375
376 DDED src/fossil.info-diff.js
--- src/finfo.c
+++ src/finfo.c
@@ -39,21 +39,21 @@
39 ** In the -p mode, there's an optional flag "-r|--revision REVISION".
40 ** The specified version (or the latest checked out version) is printed
41 ** to stdout. The -p mode is another form of the "cat" command.
42 **
43 ** Options:
44 ** -b|--brief Display a brief (one line / revision) summary
45 ** --case-sensitive B Enable or disable case-sensitive filenames. B is a
46 ** boolean: "yes", "no", "true", "false", etc.
47 ** -l|--log Select log mode (the default)
48 ** -n|--limit N Display the first N changes (default unlimited).
49 ** N less than 0 means no limit.
50 ** --offset P Skip P changes
51 ** -p|--print Select print mode
52 ** -r|--revision R Print the given revision (or ckout, if none is given)
53 ** to stdout (only in print mode)
54 ** -s|--status Select status mode (print a status indicator for FILE)
55 ** -W|--width N Width of lines (default is to auto-detect). Must be
56 ** more than 22 or else 0 to indicate no limit.
57 **
58 ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
59 */
@@ -241,11 +241,11 @@
241 ** Print on standard output the content of one or more files as they exist
242 ** in the repository. The version currently checked out is shown by default.
243 ** Other versions may be specified using the -r option.
244 **
245 ** Options:
246 ** -R|--repository REPO Extract artifacts from repository REPO
247 ** -r VERSION The specific check-in containing the file
248 **
249 ** See also: [[finfo]]
250 */
251 void cat_cmd(void){
@@ -364,11 +364,10 @@
364 url_initialize(&url, "finfo");
365 if( brBg ) url_add_parameter(&url, "brbg", 0);
366 if( uBg ) url_add_parameter(&url, "ubg", 0);
367 ridFrom = name_to_rid_www("from");
368 zPrevDate[0] = 0;
 
369 if( fnid==0 ){
370 @ No such file: %h(zFilename)
371 style_finish_page();
372 return;
373 }
374
375 DDED src/fossil.info-diff.js
--- a/src/fossil.info-diff.js
+++ b/src/fossil.info-diff.js
@@ -0,0 +1,2 @@
1
+s unsightly horiz. scrol(D.tr(),'fe!=f.lastWidth ){
2
+ pre.udiff, table.sbsdiffcols
--- a/src/fossil.info-diff.js
+++ b/src/fossil.info-diff.js
@@ -0,0 +1,2 @@
 
 
--- a/src/fossil.info-diff.js
+++ b/src/fossil.info-diff.js
@@ -0,0 +1,2 @@
1 s unsightly horiz. scrol(D.tr(),'fe!=f.lastWidth ){
2 pre.udiff, table.sbsdiffcols
+2
--- src/hook.c
+++ src/hook.c
@@ -104,10 +104,11 @@
104104
const char *zAuxFilename /* Name of auxiliary information file */
105105
){
106106
Blob r;
107107
int i;
108108
blob_init(&r, 0, 0);
109
+ if( zCmd==0 ) return 0;
109110
while( zCmd[0] ){
110111
for(i=0; zCmd[i] && zCmd[i]!='%'; i++){}
111112
blob_append(&r, zCmd, i);
112113
if( zCmd[i]==0 ) break;
113114
if( zCmd[i+1]=='F' ){
@@ -403,10 +404,11 @@
403404
while( db_step(&q)==SQLITE_ROW ){
404405
const char *zCmd = db_column_text(&q,0);
405406
char *zCmd2 = hook_subst(zCmd, zAuxFilename);
406407
int needOut = db_column_int(&q,1);
407408
Blob out;
409
+ if( zCmd2==0 ) continue;
408410
blob_init(&out,0,0);
409411
if( needOut ) hook_changes(&out, zOrigRcvid, zNewRcvid);
410412
if( bDryRun ){
411413
fossil_print("%s\n", zCmd2);
412414
if( needOut ){
413415
--- src/hook.c
+++ src/hook.c
@@ -104,10 +104,11 @@
104 const char *zAuxFilename /* Name of auxiliary information file */
105 ){
106 Blob r;
107 int i;
108 blob_init(&r, 0, 0);
 
109 while( zCmd[0] ){
110 for(i=0; zCmd[i] && zCmd[i]!='%'; i++){}
111 blob_append(&r, zCmd, i);
112 if( zCmd[i]==0 ) break;
113 if( zCmd[i+1]=='F' ){
@@ -403,10 +404,11 @@
403 while( db_step(&q)==SQLITE_ROW ){
404 const char *zCmd = db_column_text(&q,0);
405 char *zCmd2 = hook_subst(zCmd, zAuxFilename);
406 int needOut = db_column_int(&q,1);
407 Blob out;
 
408 blob_init(&out,0,0);
409 if( needOut ) hook_changes(&out, zOrigRcvid, zNewRcvid);
410 if( bDryRun ){
411 fossil_print("%s\n", zCmd2);
412 if( needOut ){
413
--- src/hook.c
+++ src/hook.c
@@ -104,10 +104,11 @@
104 const char *zAuxFilename /* Name of auxiliary information file */
105 ){
106 Blob r;
107 int i;
108 blob_init(&r, 0, 0);
109 if( zCmd==0 ) return 0;
110 while( zCmd[0] ){
111 for(i=0; zCmd[i] && zCmd[i]!='%'; i++){}
112 blob_append(&r, zCmd, i);
113 if( zCmd[i]==0 ) break;
114 if( zCmd[i+1]=='F' ){
@@ -403,10 +404,11 @@
404 while( db_step(&q)==SQLITE_ROW ){
405 const char *zCmd = db_column_text(&q,0);
406 char *zCmd2 = hook_subst(zCmd, zAuxFilename);
407 int needOut = db_column_int(&q,1);
408 Blob out;
409 if( zCmd2==0 ) continue;
410 blob_init(&out,0,0);
411 if( needOut ) hook_changes(&out, zOrigRcvid, zNewRcvid);
412 if( bDryRun ){
413 fossil_print("%s\n", zCmd2);
414 if( needOut ){
415
+47 -12
--- src/info.c
+++ src/info.c
@@ -188,11 +188,11 @@
188188
** Use the "finfo" command to get information about a specific
189189
** file in a checkout.
190190
**
191191
** Options:
192192
**
193
-** -R|--repository FILE Extract info from repository FILE
193
+** -R|--repository REPO Extract info from repository REPO
194194
** -v|--verbose Show extra information about repositories
195195
**
196196
** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]]
197197
*/
198198
void info_cmd(void){
@@ -657,12 +657,11 @@
657657
" AND event.objid=%d",
658658
rid, rid
659659
);
660660
zBrName = branch_of_rid(rid);
661661
662
- cookie_link_parameter("diff","diff","2");
663
- diffType = atoi(PD("diff","2"));
662
+ diffType = preferred_diff_type();
664663
if( db_step(&q1)==SQLITE_ROW ){
665664
const char *zUuid = db_column_text(&q1, 0);
666665
int nUuid = db_column_bytes(&q1, 0);
667666
char *zEUser, *zEComment;
668667
const char *zUser;
@@ -940,11 +939,11 @@
940939
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
941940
diffFlags,pRe,mperm);
942941
}
943942
db_finalize(&q3);
944943
append_diff_javascript(diffType==2);
945
- cookie_render();
944
+ builtin_fossil_js_bundle_or("info-diff",NULL);
946945
style_finish_page();
947946
}
948947
949948
/*
950949
** WEBPAGE: winfo
@@ -1185,13 +1184,11 @@
11851184
ReCompiled *pRe = 0;
11861185
login_check_credentials();
11871186
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
11881187
login_anonymous_available();
11891188
load_control();
1190
- cookie_link_parameter("diff","diff","2");
1191
- diffType = atoi(PD("diff","2"));
1192
- cookie_render();
1189
+ diffType = preferred_diff_type();
11931190
zRe = P("regex");
11941191
if( zRe ) re_compile(&pRe, zRe, 0);
11951192
zBranch = P("branch");
11961193
if( zBranch && zBranch[0]==0 ) zBranch = 0;
11971194
if( zBranch ){
@@ -1330,10 +1327,11 @@
13301327
}
13311328
}
13321329
manifest_destroy(pFrom);
13331330
manifest_destroy(pTo);
13341331
append_diff_javascript(diffType==2);
1332
+ builtin_fossil_js_bundle_or("info-diff",NULL);
13351333
style_finish_page();
13361334
}
13371335
13381336
#if INTERFACE
13391337
/*
@@ -1634,10 +1632,49 @@
16341632
tag_private_status(rid);
16351633
}
16361634
return objType;
16371635
}
16381636
1637
+/*
1638
+** SETTING: preferred-diff-type width=16 default=0
1639
+**
1640
+** The preferred-diff-type setting determines the preferred diff format
1641
+** for web pages if the format is not otherwise specified, for example
1642
+** by a query parameter or cookie. Allowed values:
1643
+**
1644
+** 1 Unified diff
1645
+** 2 Side-by-side diff
1646
+**
1647
+** If this setting is omitted or has a value of 0 or less, then it
1648
+** is ignored.
1649
+*/
1650
+/*
1651
+** Return the preferred diff type.
1652
+**
1653
+** 0 = No diff at all.
1654
+** 1 = unified diff
1655
+** 2 = side-by-side diff
1656
+**
1657
+** To determine the preferred diff type, the following values are
1658
+** consulted in the order shown. The first available source wins.
1659
+**
1660
+** * The "diff" query parameter
1661
+** * The "diff" field of the user display cookie
1662
+** * The "preferred-diff-type" setting
1663
+** * 1 for mobile and 2 for desktop, based on the UserAgent
1664
+*/
1665
+int preferred_diff_type(void){
1666
+ int dflt;
1667
+ char zDflt[2];
1668
+ dflt = db_get_int("preferred-diff-type",-99);
1669
+ if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1670
+ zDflt[0] = dflt + '0';
1671
+ zDflt[1] = 0;
1672
+ cookie_link_parameter("diff","diff", zDflt);
1673
+ return atoi(PD("diff",zDflt));
1674
+}
1675
+
16391676
16401677
/*
16411678
** WEBPAGE: fdiff
16421679
** URL: fdiff?v1=HASH&v2=HASH
16431680
**
@@ -1673,13 +1710,11 @@
16731710
u32 objdescFlags = 0;
16741711
int verbose = PB("verbose");
16751712
16761713
login_check_credentials();
16771714
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1678
- cookie_link_parameter("diff","diff","2");
1679
- diffType = atoi(PD("diff","2"));
1680
- cookie_render();
1715
+ diffType = preferred_diff_type();
16811716
if( P("from") && P("to") ){
16821717
v1 = artifact_from_ci_and_filename("from");
16831718
v2 = artifact_from_ci_and_filename("to");
16841719
}else{
16851720
Stmt q;
@@ -1832,12 +1867,12 @@
18321867
}
18331868
18341869
18351870
/*
18361871
** Generate a verbatim artifact as the result of an HTTP request.
1837
-** If zMime is not NULL, use it as the MIME-type. If zMime is
1838
-** NULL, guess at the MIME-type based on the filename
1872
+** If zMime is not NULL, use it as the mimetype. If zMime is
1873
+** NULL, guess at the mimetype based on the filename
18391874
** associated with the artifact.
18401875
*/
18411876
void deliver_artifact(int rid, const char *zMime){
18421877
Blob content;
18431878
const char *zAttachName = P("at");
18441879
--- src/info.c
+++ src/info.c
@@ -188,11 +188,11 @@
188 ** Use the "finfo" command to get information about a specific
189 ** file in a checkout.
190 **
191 ** Options:
192 **
193 ** -R|--repository FILE Extract info from repository FILE
194 ** -v|--verbose Show extra information about repositories
195 **
196 ** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]]
197 */
198 void info_cmd(void){
@@ -657,12 +657,11 @@
657 " AND event.objid=%d",
658 rid, rid
659 );
660 zBrName = branch_of_rid(rid);
661
662 cookie_link_parameter("diff","diff","2");
663 diffType = atoi(PD("diff","2"));
664 if( db_step(&q1)==SQLITE_ROW ){
665 const char *zUuid = db_column_text(&q1, 0);
666 int nUuid = db_column_bytes(&q1, 0);
667 char *zEUser, *zEComment;
668 const char *zUser;
@@ -940,11 +939,11 @@
940 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
941 diffFlags,pRe,mperm);
942 }
943 db_finalize(&q3);
944 append_diff_javascript(diffType==2);
945 cookie_render();
946 style_finish_page();
947 }
948
949 /*
950 ** WEBPAGE: winfo
@@ -1185,13 +1184,11 @@
1185 ReCompiled *pRe = 0;
1186 login_check_credentials();
1187 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1188 login_anonymous_available();
1189 load_control();
1190 cookie_link_parameter("diff","diff","2");
1191 diffType = atoi(PD("diff","2"));
1192 cookie_render();
1193 zRe = P("regex");
1194 if( zRe ) re_compile(&pRe, zRe, 0);
1195 zBranch = P("branch");
1196 if( zBranch && zBranch[0]==0 ) zBranch = 0;
1197 if( zBranch ){
@@ -1330,10 +1327,11 @@
1330 }
1331 }
1332 manifest_destroy(pFrom);
1333 manifest_destroy(pTo);
1334 append_diff_javascript(diffType==2);
 
1335 style_finish_page();
1336 }
1337
1338 #if INTERFACE
1339 /*
@@ -1634,10 +1632,49 @@
1634 tag_private_status(rid);
1635 }
1636 return objType;
1637 }
1638
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1639
1640 /*
1641 ** WEBPAGE: fdiff
1642 ** URL: fdiff?v1=HASH&v2=HASH
1643 **
@@ -1673,13 +1710,11 @@
1673 u32 objdescFlags = 0;
1674 int verbose = PB("verbose");
1675
1676 login_check_credentials();
1677 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1678 cookie_link_parameter("diff","diff","2");
1679 diffType = atoi(PD("diff","2"));
1680 cookie_render();
1681 if( P("from") && P("to") ){
1682 v1 = artifact_from_ci_and_filename("from");
1683 v2 = artifact_from_ci_and_filename("to");
1684 }else{
1685 Stmt q;
@@ -1832,12 +1867,12 @@
1832 }
1833
1834
1835 /*
1836 ** Generate a verbatim artifact as the result of an HTTP request.
1837 ** If zMime is not NULL, use it as the MIME-type. If zMime is
1838 ** NULL, guess at the MIME-type based on the filename
1839 ** associated with the artifact.
1840 */
1841 void deliver_artifact(int rid, const char *zMime){
1842 Blob content;
1843 const char *zAttachName = P("at");
1844
--- src/info.c
+++ src/info.c
@@ -188,11 +188,11 @@
188 ** Use the "finfo" command to get information about a specific
189 ** file in a checkout.
190 **
191 ** Options:
192 **
193 ** -R|--repository REPO Extract info from repository REPO
194 ** -v|--verbose Show extra information about repositories
195 **
196 ** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]]
197 */
198 void info_cmd(void){
@@ -657,12 +657,11 @@
657 " AND event.objid=%d",
658 rid, rid
659 );
660 zBrName = branch_of_rid(rid);
661
662 diffType = preferred_diff_type();
 
663 if( db_step(&q1)==SQLITE_ROW ){
664 const char *zUuid = db_column_text(&q1, 0);
665 int nUuid = db_column_bytes(&q1, 0);
666 char *zEUser, *zEComment;
667 const char *zUser;
@@ -940,11 +939,11 @@
939 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
940 diffFlags,pRe,mperm);
941 }
942 db_finalize(&q3);
943 append_diff_javascript(diffType==2);
944 builtin_fossil_js_bundle_or("info-diff",NULL);
945 style_finish_page();
946 }
947
948 /*
949 ** WEBPAGE: winfo
@@ -1185,13 +1184,11 @@
1184 ReCompiled *pRe = 0;
1185 login_check_credentials();
1186 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1187 login_anonymous_available();
1188 load_control();
1189 diffType = preferred_diff_type();
 
 
1190 zRe = P("regex");
1191 if( zRe ) re_compile(&pRe, zRe, 0);
1192 zBranch = P("branch");
1193 if( zBranch && zBranch[0]==0 ) zBranch = 0;
1194 if( zBranch ){
@@ -1330,10 +1327,11 @@
1327 }
1328 }
1329 manifest_destroy(pFrom);
1330 manifest_destroy(pTo);
1331 append_diff_javascript(diffType==2);
1332 builtin_fossil_js_bundle_or("info-diff",NULL);
1333 style_finish_page();
1334 }
1335
1336 #if INTERFACE
1337 /*
@@ -1634,10 +1632,49 @@
1632 tag_private_status(rid);
1633 }
1634 return objType;
1635 }
1636
1637 /*
1638 ** SETTING: preferred-diff-type width=16 default=0
1639 **
1640 ** The preferred-diff-type setting determines the preferred diff format
1641 ** for web pages if the format is not otherwise specified, for example
1642 ** by a query parameter or cookie. Allowed values:
1643 **
1644 ** 1 Unified diff
1645 ** 2 Side-by-side diff
1646 **
1647 ** If this setting is omitted or has a value of 0 or less, then it
1648 ** is ignored.
1649 */
1650 /*
1651 ** Return the preferred diff type.
1652 **
1653 ** 0 = No diff at all.
1654 ** 1 = unified diff
1655 ** 2 = side-by-side diff
1656 **
1657 ** To determine the preferred diff type, the following values are
1658 ** consulted in the order shown. The first available source wins.
1659 **
1660 ** * The "diff" query parameter
1661 ** * The "diff" field of the user display cookie
1662 ** * The "preferred-diff-type" setting
1663 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1664 */
1665 int preferred_diff_type(void){
1666 int dflt;
1667 char zDflt[2];
1668 dflt = db_get_int("preferred-diff-type",-99);
1669 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1670 zDflt[0] = dflt + '0';
1671 zDflt[1] = 0;
1672 cookie_link_parameter("diff","diff", zDflt);
1673 return atoi(PD("diff",zDflt));
1674 }
1675
1676
1677 /*
1678 ** WEBPAGE: fdiff
1679 ** URL: fdiff?v1=HASH&v2=HASH
1680 **
@@ -1673,13 +1710,11 @@
1710 u32 objdescFlags = 0;
1711 int verbose = PB("verbose");
1712
1713 login_check_credentials();
1714 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1715 diffType = preferred_diff_type();
 
 
1716 if( P("from") && P("to") ){
1717 v1 = artifact_from_ci_and_filename("from");
1718 v2 = artifact_from_ci_and_filename("to");
1719 }else{
1720 Stmt q;
@@ -1832,12 +1867,12 @@
1867 }
1868
1869
1870 /*
1871 ** Generate a verbatim artifact as the result of an HTTP request.
1872 ** If zMime is not NULL, use it as the mimetype. If zMime is
1873 ** NULL, guess at the mimetype based on the filename
1874 ** associated with the artifact.
1875 */
1876 void deliver_artifact(int rid, const char *zMime){
1877 Blob content;
1878 const char *zAttachName = P("at");
1879
--- src/json_branch.c
+++ src/json_branch.c
@@ -50,12 +50,12 @@
5050
** Impl for /json/branch/list
5151
**
5252
**
5353
** CLI mode options:
5454
**
55
-** --range X | -r X, where X is one of (open,closed,all)
56
-** (only the first letter is significant, default=open).
55
+** -r|--range X, where X is one of (open,closed,all)
56
+** (only the first letter is significant, default=open)
5757
** -a (same as --range a)
5858
** -c (same as --range c)
5959
**
6060
** HTTP mode options:
6161
**
6262
--- src/json_branch.c
+++ src/json_branch.c
@@ -50,12 +50,12 @@
50 ** Impl for /json/branch/list
51 **
52 **
53 ** CLI mode options:
54 **
55 ** --range X | -r X, where X is one of (open,closed,all)
56 ** (only the first letter is significant, default=open).
57 ** -a (same as --range a)
58 ** -c (same as --range c)
59 **
60 ** HTTP mode options:
61 **
62
--- src/json_branch.c
+++ src/json_branch.c
@@ -50,12 +50,12 @@
50 ** Impl for /json/branch/list
51 **
52 **
53 ** CLI mode options:
54 **
55 ** -r|--range X, where X is one of (open,closed,all)
56 ** (only the first letter is significant, default=open)
57 ** -a (same as --range a)
58 ** -c (same as --range c)
59 **
60 ** HTTP mode options:
61 **
62
+18
--- src/login.c
+++ src/login.c
@@ -426,10 +426,28 @@
426426
if( strncmp(zAgent, "Safari/", 7)==0 ) return 1;
427427
if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1;
428428
if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1;
429429
return 0;
430430
}
431
+
432
+/*
433
+** Make a guess at whether or not the requestor is a mobile device or
434
+** a desktop device (narrow screen vs. wide screen) based the HTTP_USER_AGENT
435
+** parameter. Return true for mobile and false for desktop.
436
+**
437
+** Caution: This is only a guess.
438
+**
439
+** Algorithm derived from https://developer.mozilla.org/en-US/docs/Web/
440
+** HTTP/Browser_detection_using_the_user_agent#mobile_device_detection on
441
+** 2021-03-01
442
+*/
443
+int user_agent_is_likely_mobile(void){
444
+ const char *zAgent = P("HTTP_USER_AGENT");
445
+ if( zAgent==0 ) return 0;
446
+ if( strstr(zAgent,"Mobi")!=0 ) return 1;
447
+ return 0;
448
+}
431449
432450
/*
433451
** COMMAND: test-ishuman
434452
**
435453
** Read lines of text from standard input. Interpret each line of text
436454
--- src/login.c
+++ src/login.c
@@ -426,10 +426,28 @@
426 if( strncmp(zAgent, "Safari/", 7)==0 ) return 1;
427 if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1;
428 if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1;
429 return 0;
430 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
432 /*
433 ** COMMAND: test-ishuman
434 **
435 ** Read lines of text from standard input. Interpret each line of text
436
--- src/login.c
+++ src/login.c
@@ -426,10 +426,28 @@
426 if( strncmp(zAgent, "Safari/", 7)==0 ) return 1;
427 if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1;
428 if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1;
429 return 0;
430 }
431
432 /*
433 ** Make a guess at whether or not the requestor is a mobile device or
434 ** a desktop device (narrow screen vs. wide screen) based the HTTP_USER_AGENT
435 ** parameter. Return true for mobile and false for desktop.
436 **
437 ** Caution: This is only a guess.
438 **
439 ** Algorithm derived from https://developer.mozilla.org/en-US/docs/Web/
440 ** HTTP/Browser_detection_using_the_user_agent#mobile_device_detection on
441 ** 2021-03-01
442 */
443 int user_agent_is_likely_mobile(void){
444 const char *zAgent = P("HTTP_USER_AGENT");
445 if( zAgent==0 ) return 0;
446 if( strstr(zAgent,"Mobi")!=0 ) return 1;
447 return 0;
448 }
449
450 /*
451 ** COMMAND: test-ishuman
452 **
453 ** Read lines of text from standard input. Interpret each line of text
454
+1 -1
--- src/lookslike.c
+++ src/lookslike.c
@@ -401,11 +401,11 @@
401401
**
402402
** Usage: %fossil test-looks-like-utf FILENAME
403403
**
404404
** Options:
405405
** -n|--limit N Repeat looks-like function N times, for
406
-** performance measurement. Default = 1;
406
+** performance measurement. Default = 1
407407
** --utf8 Ignoring BOM and file size, force UTF-8 checking
408408
** --utf16 Ignoring BOM and file size, force UTF-16 checking
409409
**
410410
** FILENAME is the name of a file to check for textual content in the UTF-8
411411
** and/or UTF-16 encodings.
412412
--- src/lookslike.c
+++ src/lookslike.c
@@ -401,11 +401,11 @@
401 **
402 ** Usage: %fossil test-looks-like-utf FILENAME
403 **
404 ** Options:
405 ** -n|--limit N Repeat looks-like function N times, for
406 ** performance measurement. Default = 1;
407 ** --utf8 Ignoring BOM and file size, force UTF-8 checking
408 ** --utf16 Ignoring BOM and file size, force UTF-16 checking
409 **
410 ** FILENAME is the name of a file to check for textual content in the UTF-8
411 ** and/or UTF-16 encodings.
412
--- src/lookslike.c
+++ src/lookslike.c
@@ -401,11 +401,11 @@
401 **
402 ** Usage: %fossil test-looks-like-utf FILENAME
403 **
404 ** Options:
405 ** -n|--limit N Repeat looks-like function N times, for
406 ** performance measurement. Default = 1
407 ** --utf8 Ignoring BOM and file size, force UTF-8 checking
408 ** --utf16 Ignoring BOM and file size, force UTF-16 checking
409 **
410 ** FILENAME is the name of a file to check for textual content in the UTF-8
411 ** and/or UTF-16 encodings.
412
+10 -27
--- src/main.c
+++ src/main.c
@@ -679,14 +679,19 @@
679679
}
680680
#endif
681681
682682
fossil_printf_selfcheck();
683683
fossil_limit_memory(1);
684
+
685
+ /* When updating the minimum SQLite version, change the number here,
686
+ ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take
687
+ ** care that both places agree! */
684688
if( sqlite3_libversion_number()<3035000 ){
685689
fossil_panic("Unsuitable SQLite version %s, must be at least 3.35.0",
686690
sqlite3_libversion());
687691
}
692
+
688693
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
689694
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
690695
memset(&g, 0, sizeof(g));
691696
g.now = time(0);
692697
g.httpHeader = empty_blob;
@@ -1838,33 +1843,11 @@
18381843
zPathInfo += 7;
18391844
g.nExtraURL += 7;
18401845
cgi_replace_parameter("PATH_INFO", zPathInfo);
18411846
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
18421847
etag_cancel();
1843
- }else if( zPathInfo && strncmp(zPathInfo, "/skn_", 5)==0 ){
1844
- int i;
1845
- char *zAlt;
1846
- char *zErr;
1847
- char *z;
1848
- while( (z = strstr(zPathInfo+1,"/skn_"))!=0 ) zPathInfo = z;
1849
- for(i=5; zPathInfo[i] && zPathInfo[i]!='/'; i++){}
1850
- zAlt = mprintf("%.*s", i-5, zPathInfo+5);
1851
- zErr = skin_use_alternative(zAlt);
1852
- if( zErr ){
1853
- fossil_free(zErr);
1854
- }else{
1855
- char *zNewScript;
1856
- zNewScript = mprintf("%T/skn_%s", P("SCRIPT_NAME"), zAlt);
1857
- if( g.zTop ) g.zTop = mprintf("%R/skn_%s", zAlt);
1858
- if( g.zBaseURL ) g.zBaseURL = mprintf("%s/skn_%s", g.zBaseURL, zAlt);
1859
- zPathInfo += i;
1860
- g.nExtraURL += i;
1861
- cgi_replace_parameter("PATH_INFO", zPathInfo);
1862
- cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1863
- }
1864
- fossil_free(zAlt);
1865
- }
1848
+ }
18661849
18671850
/* If the content type is application/x-fossil or
18681851
** application/x-fossil-debug, then a sync/push/pull/clone is
18691852
** desired, so default the PATH_INFO to /xfer
18701853
*/
@@ -2182,11 +2165,11 @@
21822165
** setenv: NAME VALUE Set environment variable NAME to VALUE. Or
21832166
** if VALUE is omitted, unset NAME.
21842167
**
21852168
** HOME: PATH Shorthand for "setenv: HOME PATH"
21862169
**
2187
-** debug: FILE Causing debugging information to be written
2170
+** cgi-debug: FILE Causing debugging information to be written
21882171
** into FILE.
21892172
**
21902173
** errorlog: FILE Warnings, errors, and panics written to FILE.
21912174
**
21922175
** timeout: SECONDS Do not run for longer than SECONDS. The default
@@ -2378,11 +2361,11 @@
23782361
** Use one of the built-in skins defined by LABEL. LABEL is the
23792362
** name of the subdirectory under the skins/ directory that holds
23802363
** the elements of the built-in skin. If LABEL does not match,
23812364
** this directive is a silent no-op.
23822365
*/
2383
- skin_use_alternative(blob_str(&value));
2366
+ fossil_free(skin_use_alternative(blob_str(&value), 1));
23842367
blob_reset(&value);
23852368
continue;
23862369
}
23872370
if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){
23882371
/* jsmode: MODE
@@ -2737,12 +2720,12 @@
27372720
** COMMAND: test-http
27382721
**
27392722
** Works like the [[http]] command but gives setup permission to all users.
27402723
**
27412724
** Options:
2742
-** --th-trace trace TH1 execution (for debugging purposes)
2743
-** --usercap CAP user capability string. (Default: "sx")
2725
+** --th-trace Trace TH1 execution (for debugging purposes)
2726
+** --usercap CAP User capability string (Default: "sx")
27442727
**
27452728
*/
27462729
void cmd_test_http(void){
27472730
const char *zIpAddr; /* IP address of remote client */
27482731
const char *zUserCap;
27492732
--- src/main.c
+++ src/main.c
@@ -679,14 +679,19 @@
679 }
680 #endif
681
682 fossil_printf_selfcheck();
683 fossil_limit_memory(1);
 
 
 
 
684 if( sqlite3_libversion_number()<3035000 ){
685 fossil_panic("Unsuitable SQLite version %s, must be at least 3.35.0",
686 sqlite3_libversion());
687 }
 
688 sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
689 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
690 memset(&g, 0, sizeof(g));
691 g.now = time(0);
692 g.httpHeader = empty_blob;
@@ -1838,33 +1843,11 @@
1838 zPathInfo += 7;
1839 g.nExtraURL += 7;
1840 cgi_replace_parameter("PATH_INFO", zPathInfo);
1841 cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1842 etag_cancel();
1843 }else if( zPathInfo && strncmp(zPathInfo, "/skn_", 5)==0 ){
1844 int i;
1845 char *zAlt;
1846 char *zErr;
1847 char *z;
1848 while( (z = strstr(zPathInfo+1,"/skn_"))!=0 ) zPathInfo = z;
1849 for(i=5; zPathInfo[i] && zPathInfo[i]!='/'; i++){}
1850 zAlt = mprintf("%.*s", i-5, zPathInfo+5);
1851 zErr = skin_use_alternative(zAlt);
1852 if( zErr ){
1853 fossil_free(zErr);
1854 }else{
1855 char *zNewScript;
1856 zNewScript = mprintf("%T/skn_%s", P("SCRIPT_NAME"), zAlt);
1857 if( g.zTop ) g.zTop = mprintf("%R/skn_%s", zAlt);
1858 if( g.zBaseURL ) g.zBaseURL = mprintf("%s/skn_%s", g.zBaseURL, zAlt);
1859 zPathInfo += i;
1860 g.nExtraURL += i;
1861 cgi_replace_parameter("PATH_INFO", zPathInfo);
1862 cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1863 }
1864 fossil_free(zAlt);
1865 }
1866
1867 /* If the content type is application/x-fossil or
1868 ** application/x-fossil-debug, then a sync/push/pull/clone is
1869 ** desired, so default the PATH_INFO to /xfer
1870 */
@@ -2182,11 +2165,11 @@
2182 ** setenv: NAME VALUE Set environment variable NAME to VALUE. Or
2183 ** if VALUE is omitted, unset NAME.
2184 **
2185 ** HOME: PATH Shorthand for "setenv: HOME PATH"
2186 **
2187 ** debug: FILE Causing debugging information to be written
2188 ** into FILE.
2189 **
2190 ** errorlog: FILE Warnings, errors, and panics written to FILE.
2191 **
2192 ** timeout: SECONDS Do not run for longer than SECONDS. The default
@@ -2378,11 +2361,11 @@
2378 ** Use one of the built-in skins defined by LABEL. LABEL is the
2379 ** name of the subdirectory under the skins/ directory that holds
2380 ** the elements of the built-in skin. If LABEL does not match,
2381 ** this directive is a silent no-op.
2382 */
2383 skin_use_alternative(blob_str(&value));
2384 blob_reset(&value);
2385 continue;
2386 }
2387 if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){
2388 /* jsmode: MODE
@@ -2737,12 +2720,12 @@
2737 ** COMMAND: test-http
2738 **
2739 ** Works like the [[http]] command but gives setup permission to all users.
2740 **
2741 ** Options:
2742 ** --th-trace trace TH1 execution (for debugging purposes)
2743 ** --usercap CAP user capability string. (Default: "sx")
2744 **
2745 */
2746 void cmd_test_http(void){
2747 const char *zIpAddr; /* IP address of remote client */
2748 const char *zUserCap;
2749
--- src/main.c
+++ src/main.c
@@ -679,14 +679,19 @@
679 }
680 #endif
681
682 fossil_printf_selfcheck();
683 fossil_limit_memory(1);
684
685 /* When updating the minimum SQLite version, change the number here,
686 ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take
687 ** care that both places agree! */
688 if( sqlite3_libversion_number()<3035000 ){
689 fossil_panic("Unsuitable SQLite version %s, must be at least 3.35.0",
690 sqlite3_libversion());
691 }
692
693 sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
694 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
695 memset(&g, 0, sizeof(g));
696 g.now = time(0);
697 g.httpHeader = empty_blob;
@@ -1838,33 +1843,11 @@
1843 zPathInfo += 7;
1844 g.nExtraURL += 7;
1845 cgi_replace_parameter("PATH_INFO", zPathInfo);
1846 cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1847 etag_cancel();
1848 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1849
1850 /* If the content type is application/x-fossil or
1851 ** application/x-fossil-debug, then a sync/push/pull/clone is
1852 ** desired, so default the PATH_INFO to /xfer
1853 */
@@ -2182,11 +2165,11 @@
2165 ** setenv: NAME VALUE Set environment variable NAME to VALUE. Or
2166 ** if VALUE is omitted, unset NAME.
2167 **
2168 ** HOME: PATH Shorthand for "setenv: HOME PATH"
2169 **
2170 ** cgi-debug: FILE Causing debugging information to be written
2171 ** into FILE.
2172 **
2173 ** errorlog: FILE Warnings, errors, and panics written to FILE.
2174 **
2175 ** timeout: SECONDS Do not run for longer than SECONDS. The default
@@ -2378,11 +2361,11 @@
2361 ** Use one of the built-in skins defined by LABEL. LABEL is the
2362 ** name of the subdirectory under the skins/ directory that holds
2363 ** the elements of the built-in skin. If LABEL does not match,
2364 ** this directive is a silent no-op.
2365 */
2366 fossil_free(skin_use_alternative(blob_str(&value), 1));
2367 blob_reset(&value);
2368 continue;
2369 }
2370 if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){
2371 /* jsmode: MODE
@@ -2737,12 +2720,12 @@
2720 ** COMMAND: test-http
2721 **
2722 ** Works like the [[http]] command but gives setup permission to all users.
2723 **
2724 ** Options:
2725 ** --th-trace Trace TH1 execution (for debugging purposes)
2726 ** --usercap CAP User capability string (Default: "sx")
2727 **
2728 */
2729 void cmd_test_http(void){
2730 const char *zIpAddr; /* IP address of remote client */
2731 const char *zUserCap;
2732
--- src/main.mk
+++ src/main.mk
@@ -225,10 +225,11 @@
225225
$(SRCDIR)/fossil.bootstrap.js \
226226
$(SRCDIR)/fossil.confirmer.js \
227227
$(SRCDIR)/fossil.copybutton.js \
228228
$(SRCDIR)/fossil.dom.js \
229229
$(SRCDIR)/fossil.fetch.js \
230
+ $(SRCDIR)/fossil.info-diff.js \
230231
$(SRCDIR)/fossil.numbered-lines.js \
231232
$(SRCDIR)/fossil.page.fileedit.js \
232233
$(SRCDIR)/fossil.page.forumpost.js \
233234
$(SRCDIR)/fossil.page.pikchrshow.js \
234235
$(SRCDIR)/fossil.page.whistory.js \
235236
--- src/main.mk
+++ src/main.mk
@@ -225,10 +225,11 @@
225 $(SRCDIR)/fossil.bootstrap.js \
226 $(SRCDIR)/fossil.confirmer.js \
227 $(SRCDIR)/fossil.copybutton.js \
228 $(SRCDIR)/fossil.dom.js \
229 $(SRCDIR)/fossil.fetch.js \
 
230 $(SRCDIR)/fossil.numbered-lines.js \
231 $(SRCDIR)/fossil.page.fileedit.js \
232 $(SRCDIR)/fossil.page.forumpost.js \
233 $(SRCDIR)/fossil.page.pikchrshow.js \
234 $(SRCDIR)/fossil.page.whistory.js \
235
--- src/main.mk
+++ src/main.mk
@@ -225,10 +225,11 @@
225 $(SRCDIR)/fossil.bootstrap.js \
226 $(SRCDIR)/fossil.confirmer.js \
227 $(SRCDIR)/fossil.copybutton.js \
228 $(SRCDIR)/fossil.dom.js \
229 $(SRCDIR)/fossil.fetch.js \
230 $(SRCDIR)/fossil.info-diff.js \
231 $(SRCDIR)/fossil.numbered-lines.js \
232 $(SRCDIR)/fossil.page.fileedit.js \
233 $(SRCDIR)/fossil.page.forumpost.js \
234 $(SRCDIR)/fossil.page.pikchrshow.js \
235 $(SRCDIR)/fossil.page.whistory.js \
236
--- src/main.mk
+++ src/main.mk
@@ -225,10 +225,11 @@
225225
$(SRCDIR)/fossil.bootstrap.js \
226226
$(SRCDIR)/fossil.confirmer.js \
227227
$(SRCDIR)/fossil.copybutton.js \
228228
$(SRCDIR)/fossil.dom.js \
229229
$(SRCDIR)/fossil.fetch.js \
230
+ $(SRCDIR)/fossil.info-diff.js \
230231
$(SRCDIR)/fossil.numbered-lines.js \
231232
$(SRCDIR)/fossil.page.fileedit.js \
232233
$(SRCDIR)/fossil.page.forumpost.js \
233234
$(SRCDIR)/fossil.page.pikchrshow.js \
234235
$(SRCDIR)/fossil.page.whistory.js \
235236
--- src/main.mk
+++ src/main.mk
@@ -225,10 +225,11 @@
225 $(SRCDIR)/fossil.bootstrap.js \
226 $(SRCDIR)/fossil.confirmer.js \
227 $(SRCDIR)/fossil.copybutton.js \
228 $(SRCDIR)/fossil.dom.js \
229 $(SRCDIR)/fossil.fetch.js \
 
230 $(SRCDIR)/fossil.numbered-lines.js \
231 $(SRCDIR)/fossil.page.fileedit.js \
232 $(SRCDIR)/fossil.page.forumpost.js \
233 $(SRCDIR)/fossil.page.pikchrshow.js \
234 $(SRCDIR)/fossil.page.whistory.js \
235
--- src/main.mk
+++ src/main.mk
@@ -225,10 +225,11 @@
225 $(SRCDIR)/fossil.bootstrap.js \
226 $(SRCDIR)/fossil.confirmer.js \
227 $(SRCDIR)/fossil.copybutton.js \
228 $(SRCDIR)/fossil.dom.js \
229 $(SRCDIR)/fossil.fetch.js \
230 $(SRCDIR)/fossil.info-diff.js \
231 $(SRCDIR)/fossil.numbered-lines.js \
232 $(SRCDIR)/fossil.page.fileedit.js \
233 $(SRCDIR)/fossil.page.forumpost.js \
234 $(SRCDIR)/fossil.page.pikchrshow.js \
235 $(SRCDIR)/fossil.page.whistory.js \
236
+1 -1
--- src/manifest.c
+++ src/manifest.c
@@ -1281,11 +1281,11 @@
12811281
** repositories after making any changes to the manifest_parse()
12821282
** implementation to confirm that the changes did not break anything.
12831283
**
12841284
** Options:
12851285
**
1286
-** --limit N Parse no more than N artifacts before stopping.
1286
+** --limit N Parse no more than N artifacts before stopping
12871287
** --wellformed Use all BLOB table entries as input, not just
12881288
** those entries that are believed to be valid
12891289
** artifacts, and verify that the result the
12901290
** manifest_is_well_formed() agrees with the
12911291
** result of manifest_parse().
12921292
--- src/manifest.c
+++ src/manifest.c
@@ -1281,11 +1281,11 @@
1281 ** repositories after making any changes to the manifest_parse()
1282 ** implementation to confirm that the changes did not break anything.
1283 **
1284 ** Options:
1285 **
1286 ** --limit N Parse no more than N artifacts before stopping.
1287 ** --wellformed Use all BLOB table entries as input, not just
1288 ** those entries that are believed to be valid
1289 ** artifacts, and verify that the result the
1290 ** manifest_is_well_formed() agrees with the
1291 ** result of manifest_parse().
1292
--- src/manifest.c
+++ src/manifest.c
@@ -1281,11 +1281,11 @@
1281 ** repositories after making any changes to the manifest_parse()
1282 ** implementation to confirm that the changes did not break anything.
1283 **
1284 ** Options:
1285 **
1286 ** --limit N Parse no more than N artifacts before stopping
1287 ** --wellformed Use all BLOB table entries as input, not just
1288 ** those entries that are believed to be valid
1289 ** artifacts, and verify that the result the
1290 ** manifest_is_well_formed() agrees with the
1291 ** result of manifest_parse().
1292
--- src/merge3.c
+++ src/merge3.c
@@ -317,10 +317,11 @@
317317
nConflict++;
318318
while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
319319
sz++;
320320
}
321321
DEBUG( printf("CONFLICT %d\n", sz); )
322
+ ensure_line_end(pOut, useCrLf);
322323
blob_append(pOut, mergeMarker[0], -1);
323324
ensure_line_end(pOut, useCrLf);
324325
i1 = output_one_side(pOut, pV1, aC1, i1, sz);
325326
ensure_line_end(pOut, useCrLf);
326327
blob_append(pOut, mergeMarker[1], -1);
327328
--- src/merge3.c
+++ src/merge3.c
@@ -317,10 +317,11 @@
317 nConflict++;
318 while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
319 sz++;
320 }
321 DEBUG( printf("CONFLICT %d\n", sz); )
 
322 blob_append(pOut, mergeMarker[0], -1);
323 ensure_line_end(pOut, useCrLf);
324 i1 = output_one_side(pOut, pV1, aC1, i1, sz);
325 ensure_line_end(pOut, useCrLf);
326 blob_append(pOut, mergeMarker[1], -1);
327
--- src/merge3.c
+++ src/merge3.c
@@ -317,10 +317,11 @@
317 nConflict++;
318 while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
319 sz++;
320 }
321 DEBUG( printf("CONFLICT %d\n", sz); )
322 ensure_line_end(pOut, useCrLf);
323 blob_append(pOut, mergeMarker[0], -1);
324 ensure_line_end(pOut, useCrLf);
325 i1 = output_one_side(pOut, pV1, aC1, i1, sz);
326 ensure_line_end(pOut, useCrLf);
327 blob_append(pOut, mergeMarker[1], -1);
328
+1 -1
--- src/name.c
+++ src/name.c
@@ -909,11 +909,11 @@
909909
** plays.
910910
**
911911
** Options:
912912
**
913913
** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't',
914
-** 'w', 'g', or 'e').
914
+** 'w', 'g', or 'e')
915915
** -v|--verbose Provide extra information (such as the RID)
916916
*/
917917
void whatis_cmd(void){
918918
int rid;
919919
const char *zName;
920920
--- src/name.c
+++ src/name.c
@@ -909,11 +909,11 @@
909 ** plays.
910 **
911 ** Options:
912 **
913 ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't',
914 ** 'w', 'g', or 'e').
915 ** -v|--verbose Provide extra information (such as the RID)
916 */
917 void whatis_cmd(void){
918 int rid;
919 const char *zName;
920
--- src/name.c
+++ src/name.c
@@ -909,11 +909,11 @@
909 ** plays.
910 **
911 ** Options:
912 **
913 ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't',
914 ** 'w', 'g', or 'e')
915 ** -v|--verbose Provide extra information (such as the RID)
916 */
917 void whatis_cmd(void){
918 int rid;
919 const char *zName;
920
+12 -12
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -389,29 +389,29 @@
389389
** resp. stdout, and the names "-" can be used as aliases for those
390390
** streams.
391391
**
392392
** Options:
393393
**
394
-** -div On success, adds a DIV wrapper around the
394
+** -div On success, add a DIV wrapper around the
395395
** resulting SVG output which limits its max-width to
396396
** its computed maximum ideal size.
397397
**
398
-** -div-indent Like -div but indents the div.
399
-**
400
-** -div-center Like -div but centers the div.
401
-**
402
-** -div-left Like -div but floats the div left.
403
-**
404
-** -div-right Like -div but floats the div right.
405
-**
406
-** -div-toggle Sets the 'toggle' CSS class on the div (used by the
398
+** -div-indent Like -div but indent the div.
399
+**
400
+** -div-center Like -div but center the div.
401
+**
402
+** -div-left Like -div but float the div left.
403
+**
404
+** -div-right Like -div but float the div right.
405
+**
406
+** -div-toggle Set the 'toggle' CSS class on the div (used by the
407407
** JavaScript-side post-processor).
408408
**
409
-** -div-source Sets the 'source' CSS class on the div, which tells
409
+** -div-source Set the 'source' CSS class on the div, which tells
410410
** CSS to hide the SVG and reveal the source by default.
411411
**
412
-** -src Stores the input pikchr's source code in the output as
412
+** -src Store the input pikchr's source code in the output as
413413
** a separate element adjacent to the SVG one. Implied
414414
** by -div-source.
415415
**
416416
**
417417
** -th Process the input using TH1 before passing it to pikchr.
418418
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -389,29 +389,29 @@
389 ** resp. stdout, and the names "-" can be used as aliases for those
390 ** streams.
391 **
392 ** Options:
393 **
394 ** -div On success, adds a DIV wrapper around the
395 ** resulting SVG output which limits its max-width to
396 ** its computed maximum ideal size.
397 **
398 ** -div-indent Like -div but indents the div.
399 **
400 ** -div-center Like -div but centers the div.
401 **
402 ** -div-left Like -div but floats the div left.
403 **
404 ** -div-right Like -div but floats the div right.
405 **
406 ** -div-toggle Sets the 'toggle' CSS class on the div (used by the
407 ** JavaScript-side post-processor).
408 **
409 ** -div-source Sets the 'source' CSS class on the div, which tells
410 ** CSS to hide the SVG and reveal the source by default.
411 **
412 ** -src Stores the input pikchr's source code in the output as
413 ** a separate element adjacent to the SVG one. Implied
414 ** by -div-source.
415 **
416 **
417 ** -th Process the input using TH1 before passing it to pikchr.
418
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -389,29 +389,29 @@
389 ** resp. stdout, and the names "-" can be used as aliases for those
390 ** streams.
391 **
392 ** Options:
393 **
394 ** -div On success, add a DIV wrapper around the
395 ** resulting SVG output which limits its max-width to
396 ** its computed maximum ideal size.
397 **
398 ** -div-indent Like -div but indent the div.
399 **
400 ** -div-center Like -div but center the div.
401 **
402 ** -div-left Like -div but float the div left.
403 **
404 ** -div-right Like -div but float the div right.
405 **
406 ** -div-toggle Set the 'toggle' CSS class on the div (used by the
407 ** JavaScript-side post-processor).
408 **
409 ** -div-source Set the 'source' CSS class on the div, which tells
410 ** CSS to hide the SVG and reveal the source by default.
411 **
412 ** -src Store the input pikchr's source code in the output as
413 ** a separate element adjacent to the SVG one. Implied
414 ** by -div-source.
415 **
416 **
417 ** -th Process the input using TH1 before passing it to pikchr.
418
+1 -1
--- src/purge.c
+++ src/purge.c
@@ -505,11 +505,11 @@
505505
**
506506
** TBD...
507507
**
508508
** COMMON OPTIONS:
509509
**
510
-** --explain Make no changes, but show what would happen.
510
+** --explain Make no changes, but show what would happen
511511
** --dry-run An alias for --explain
512512
*/
513513
void purge_cmd(void){
514514
int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
515515
const char *zSubcmd;
516516
--- src/purge.c
+++ src/purge.c
@@ -505,11 +505,11 @@
505 **
506 ** TBD...
507 **
508 ** COMMON OPTIONS:
509 **
510 ** --explain Make no changes, but show what would happen.
511 ** --dry-run An alias for --explain
512 */
513 void purge_cmd(void){
514 int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
515 const char *zSubcmd;
516
--- src/purge.c
+++ src/purge.c
@@ -505,11 +505,11 @@
505 **
506 ** TBD...
507 **
508 ** COMMON OPTIONS:
509 **
510 ** --explain Make no changes, but show what would happen
511 ** --dry-run An alias for --explain
512 */
513 void purge_cmd(void){
514 int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
515 const char *zSubcmd;
516
+5 -5
--- src/rebuild.c
+++ src/rebuild.c
@@ -889,13 +889,13 @@
889889
**
890890
** The user is prompted to confirm the scrub unless the --force option
891891
** is used.
892892
**
893893
** Options:
894
-** --force do not prompt for confirmation
895
-** --private only private branches are removed from the repository
896
-** --verily scrub real thoroughly (see above)
894
+** --force Do not prompt for confirmation
895
+** --private Only private branches are removed from the repository
896
+** --verily Scrub real thoroughly (see above)
897897
*/
898898
void scrub_cmd(void){
899899
int bVerily = find_option("verily",0,0)!=0;
900900
int bForce = find_option("force", "f", 0)!=0;
901901
int privateOnly = find_option("private",0,0)!=0;
@@ -1114,11 +1114,11 @@
11141114
**
11151115
** No files or directories will be created.
11161116
**
11171117
** Options:
11181118
** -L|--prefixlength N Set the length of the names of the DESTINATION
1119
-** subdirectories to N.
1119
+** subdirectories to N
11201120
*/
11211121
void test_hash_from_path_cmd(void) {
11221122
char *zDest;
11231123
char *zUuid;
11241124
char *zFile;
@@ -1284,11 +1284,11 @@
12841284
** 40+ character artifact ID, AA the first 2 characters.
12851285
** If -L|--prefixlength is given, the length (default 2) of the directory prefix
12861286
** can be set to 0,1,..,9 characters.
12871287
**
12881288
** Options:
1289
-** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
1289
+** -R|--repository REPO Deconstruct given REPOSITORY.
12901290
** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
12911291
** the file .rid1 in the DESTINATION directory.
12921292
** -L|--prefixlength N Set the length of the names of the DESTINATION
12931293
** subdirectories to N.
12941294
** --private Include private artifacts.
12951295
--- src/rebuild.c
+++ src/rebuild.c
@@ -889,13 +889,13 @@
889 **
890 ** The user is prompted to confirm the scrub unless the --force option
891 ** is used.
892 **
893 ** Options:
894 ** --force do not prompt for confirmation
895 ** --private only private branches are removed from the repository
896 ** --verily scrub real thoroughly (see above)
897 */
898 void scrub_cmd(void){
899 int bVerily = find_option("verily",0,0)!=0;
900 int bForce = find_option("force", "f", 0)!=0;
901 int privateOnly = find_option("private",0,0)!=0;
@@ -1114,11 +1114,11 @@
1114 **
1115 ** No files or directories will be created.
1116 **
1117 ** Options:
1118 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1119 ** subdirectories to N.
1120 */
1121 void test_hash_from_path_cmd(void) {
1122 char *zDest;
1123 char *zUuid;
1124 char *zFile;
@@ -1284,11 +1284,11 @@
1284 ** 40+ character artifact ID, AA the first 2 characters.
1285 ** If -L|--prefixlength is given, the length (default 2) of the directory prefix
1286 ** can be set to 0,1,..,9 characters.
1287 **
1288 ** Options:
1289 ** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
1290 ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
1291 ** the file .rid1 in the DESTINATION directory.
1292 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1293 ** subdirectories to N.
1294 ** --private Include private artifacts.
1295
--- src/rebuild.c
+++ src/rebuild.c
@@ -889,13 +889,13 @@
889 **
890 ** The user is prompted to confirm the scrub unless the --force option
891 ** is used.
892 **
893 ** Options:
894 ** --force Do not prompt for confirmation
895 ** --private Only private branches are removed from the repository
896 ** --verily Scrub real thoroughly (see above)
897 */
898 void scrub_cmd(void){
899 int bVerily = find_option("verily",0,0)!=0;
900 int bForce = find_option("force", "f", 0)!=0;
901 int privateOnly = find_option("private",0,0)!=0;
@@ -1114,11 +1114,11 @@
1114 **
1115 ** No files or directories will be created.
1116 **
1117 ** Options:
1118 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1119 ** subdirectories to N
1120 */
1121 void test_hash_from_path_cmd(void) {
1122 char *zDest;
1123 char *zUuid;
1124 char *zFile;
@@ -1284,11 +1284,11 @@
1284 ** 40+ character artifact ID, AA the first 2 characters.
1285 ** If -L|--prefixlength is given, the length (default 2) of the directory prefix
1286 ** can be set to 0,1,..,9 characters.
1287 **
1288 ** Options:
1289 ** -R|--repository REPO Deconstruct given REPOSITORY.
1290 ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
1291 ** the file .rid1 in the DESTINATION directory.
1292 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1293 ** subdirectories to N.
1294 ** --private Include private artifacts.
1295
+6 -6
--- src/rss.c
+++ src/rss.c
@@ -224,28 +224,28 @@
224224
** Usage: %fossil rss ?OPTIONS?
225225
**
226226
** The CLI variant of the /timeline.rss page, this produces an RSS
227227
** feed of the timeline to stdout. Options:
228228
**
229
-** -type|y FLAG may be: all (default), ci (show check-ins only),
229
+** -type|y FLAG May be: all (default), ci (show check-ins only),
230230
** t (show tickets only), w (show wiki only).
231231
**
232232
** -limit|n LIMIT The maximum number of items to show.
233233
**
234
-** -tkt HASH Filters for only those events for the specified ticket.
234
+** -tkt HASH Filter for only those events for the specified ticket.
235235
**
236
-** -tag TAG filters for a tag
236
+** -tag TAG Filter for a tag
237237
**
238
-** -wiki NAME Filters on a specific wiki page.
238
+** -wiki NAME Filter on a specific wiki page.
239239
**
240240
** Only one of -tkt, -tag, or -wiki may be used.
241241
**
242
-** -name FILENAME filters for a specific file. This may be combined
242
+** -name FILENAME Filter for a specific file. This may be combined
243243
** with one of the other filters (useful for looking
244244
** at a specific branch).
245245
**
246
-** -url STRING Sets the RSS feed's root URL to the given string.
246
+** -url STRING Set the RSS feed's root URL to the given string.
247247
** The default is "URL-PLACEHOLDER" (without quotes).
248248
*/
249249
void cmd_timeline_rss(void){
250250
Stmt q;
251251
int nLine=0;
252252
--- src/rss.c
+++ src/rss.c
@@ -224,28 +224,28 @@
224 ** Usage: %fossil rss ?OPTIONS?
225 **
226 ** The CLI variant of the /timeline.rss page, this produces an RSS
227 ** feed of the timeline to stdout. Options:
228 **
229 ** -type|y FLAG may be: all (default), ci (show check-ins only),
230 ** t (show tickets only), w (show wiki only).
231 **
232 ** -limit|n LIMIT The maximum number of items to show.
233 **
234 ** -tkt HASH Filters for only those events for the specified ticket.
235 **
236 ** -tag TAG filters for a tag
237 **
238 ** -wiki NAME Filters on a specific wiki page.
239 **
240 ** Only one of -tkt, -tag, or -wiki may be used.
241 **
242 ** -name FILENAME filters for a specific file. This may be combined
243 ** with one of the other filters (useful for looking
244 ** at a specific branch).
245 **
246 ** -url STRING Sets the RSS feed's root URL to the given string.
247 ** The default is "URL-PLACEHOLDER" (without quotes).
248 */
249 void cmd_timeline_rss(void){
250 Stmt q;
251 int nLine=0;
252
--- src/rss.c
+++ src/rss.c
@@ -224,28 +224,28 @@
224 ** Usage: %fossil rss ?OPTIONS?
225 **
226 ** The CLI variant of the /timeline.rss page, this produces an RSS
227 ** feed of the timeline to stdout. Options:
228 **
229 ** -type|y FLAG May be: all (default), ci (show check-ins only),
230 ** t (show tickets only), w (show wiki only).
231 **
232 ** -limit|n LIMIT The maximum number of items to show.
233 **
234 ** -tkt HASH Filter for only those events for the specified ticket.
235 **
236 ** -tag TAG Filter for a tag
237 **
238 ** -wiki NAME Filter on a specific wiki page.
239 **
240 ** Only one of -tkt, -tag, or -wiki may be used.
241 **
242 ** -name FILENAME Filter for a specific file. This may be combined
243 ** with one of the other filters (useful for looking
244 ** at a specific branch).
245 **
246 ** -url STRING Set the RSS feed's root URL to the given string.
247 ** The default is "URL-PLACEHOLDER" (without quotes).
248 */
249 void cmd_timeline_rss(void){
250 Stmt q;
251 int nLine=0;
252
+55 -3
--- src/setup.c
+++ src/setup.c
@@ -952,10 +952,62 @@
952952
@ </td></tr></table>
953953
@ </div></form>
954954
db_end_transaction(0);
955955
style_finish_page();
956956
}
957
+
958
+/*
959
+** SETTING: mainmenu width=70 block-text
960
+**
961
+** The mainmenu setting specifies the entries on the main menu
962
+** for many skins. The mainmenu should be a TCL list. Each set
963
+** of four consecutive values defines a single main menu item:
964
+**
965
+** * The first term is text that appears on the menu.
966
+**
967
+** * The second term is a hyperlink to take when a user clicks on the
968
+** entry. Hyperlinks that start with "/" are relative to the
969
+** repository root.
970
+**
971
+** * The third term is an argument to the TH1 "capexpr" command.
972
+** If capexpr evalutes to true, then the entry is shown. If not,
973
+** the entry is omitted. "*" is always true. "{}" is never true.
974
+**
975
+** * The fourth term is a list of extra class names to apply to the new
976
+** menu entry. Some skins will classes "desktoponly" and "wideonly"
977
+** to only show the entries when the web browser screen is wide or
978
+** very wide, respectively.
979
+**
980
+** Some custom skins might not use this property. Whether the property
981
+** is used or not a choice made by the skin designer. Some skins may add
982
+** extra choices (such as the hamburger button) to the menu.
983
+*/
984
+/*
985
+** SETTING: sitemap-extra width=70 block-text
986
+**
987
+** The sitemap-extra setting defines extra links to appear on the
988
+** /sitemap web page as sub-items of the "Home Page" entry before the
989
+** "Documentation Search" entry (if any). For skins that use the /sitemap
990
+** page to construct a hamburger menu dropdown, new entries added here
991
+** will appear on the hamburger menu.
992
+**
993
+** This setting should be a TCL list divided into triples. Each
994
+** triple defines a new entry:
995
+**
996
+** * The first term is the display name of the /sitemap entry
997
+**
998
+** * The second term is a hyperlink to take when a user clicks on the
999
+** entry. Hyperlinks that start with "/" are relative to the
1000
+** repository root.
1001
+**
1002
+** * The third term is an argument to the TH1 "capexpr" command.
1003
+** If capexpr evalutes to true, then the entry is shown. If not,
1004
+** the entry is omitted. "*" is always true.
1005
+**
1006
+** The default value is blank, meaning no added entries.
1007
+*/
1008
+
9571009
9581010
/*
9591011
** WEBPAGE: setup_config
9601012
**
9611013
** The "Admin/Configuration" page. Requires Setup privilege.
@@ -1026,11 +1078,11 @@
10261078
@ leading "/".</p>
10271079
@ <p>(Property: "index-page")
10281080
@ <hr>
10291081
@ <p>The main menu for the web interface
10301082
@ <p>
1031
- @
1083
+ @
10321084
@ <p>This setting should be a TCL list. Each set of four consecutive
10331085
@ values defines a single main menu item:
10341086
@ <ol>
10351087
@ <li> The first term is text that appears on the menu.
10361088
@ <li> The second term is a hyperlink to take when a user clicks on the
@@ -1043,12 +1095,12 @@
10431095
@ menu entry. Some skins will classes "desktoponly" and "wideonly"
10441096
@ to only show the entries when the web browser screen is wide or
10451097
@ very wide, respectively.
10461098
@ </ol>
10471099
@
1048
- @ <p>Some custom skins might not use this property. Whether the property
1049
- @ is used or a choice made by the skin designer. Some skins add an extra
1100
+ @ <p>Some custom skins might not use this property. Whether the property
1101
+ @ is used or not a choice made by the skin designer. Some skins may add extra
10501102
@ choices (such as the hamburger button) to the menu that are not shown
10511103
@ on this list. (Property: mainmenu)
10521104
@ <p>
10531105
if(P("resetMenu")!=0){
10541106
db_unset("mainmenu", 0);
10551107
--- src/setup.c
+++ src/setup.c
@@ -952,10 +952,62 @@
952 @ </td></tr></table>
953 @ </div></form>
954 db_end_transaction(0);
955 style_finish_page();
956 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
957
958 /*
959 ** WEBPAGE: setup_config
960 **
961 ** The "Admin/Configuration" page. Requires Setup privilege.
@@ -1026,11 +1078,11 @@
1026 @ leading "/".</p>
1027 @ <p>(Property: "index-page")
1028 @ <hr>
1029 @ <p>The main menu for the web interface
1030 @ <p>
1031 @
1032 @ <p>This setting should be a TCL list. Each set of four consecutive
1033 @ values defines a single main menu item:
1034 @ <ol>
1035 @ <li> The first term is text that appears on the menu.
1036 @ <li> The second term is a hyperlink to take when a user clicks on the
@@ -1043,12 +1095,12 @@
1043 @ menu entry. Some skins will classes "desktoponly" and "wideonly"
1044 @ to only show the entries when the web browser screen is wide or
1045 @ very wide, respectively.
1046 @ </ol>
1047 @
1048 @ <p>Some custom skins might not use this property. Whether the property
1049 @ is used or a choice made by the skin designer. Some skins add an extra
1050 @ choices (such as the hamburger button) to the menu that are not shown
1051 @ on this list. (Property: mainmenu)
1052 @ <p>
1053 if(P("resetMenu")!=0){
1054 db_unset("mainmenu", 0);
1055
--- src/setup.c
+++ src/setup.c
@@ -952,10 +952,62 @@
952 @ </td></tr></table>
953 @ </div></form>
954 db_end_transaction(0);
955 style_finish_page();
956 }
957
958 /*
959 ** SETTING: mainmenu width=70 block-text
960 **
961 ** The mainmenu setting specifies the entries on the main menu
962 ** for many skins. The mainmenu should be a TCL list. Each set
963 ** of four consecutive values defines a single main menu item:
964 **
965 ** * The first term is text that appears on the menu.
966 **
967 ** * The second term is a hyperlink to take when a user clicks on the
968 ** entry. Hyperlinks that start with "/" are relative to the
969 ** repository root.
970 **
971 ** * The third term is an argument to the TH1 "capexpr" command.
972 ** If capexpr evalutes to true, then the entry is shown. If not,
973 ** the entry is omitted. "*" is always true. "{}" is never true.
974 **
975 ** * The fourth term is a list of extra class names to apply to the new
976 ** menu entry. Some skins will classes "desktoponly" and "wideonly"
977 ** to only show the entries when the web browser screen is wide or
978 ** very wide, respectively.
979 **
980 ** Some custom skins might not use this property. Whether the property
981 ** is used or not a choice made by the skin designer. Some skins may add
982 ** extra choices (such as the hamburger button) to the menu.
983 */
984 /*
985 ** SETTING: sitemap-extra width=70 block-text
986 **
987 ** The sitemap-extra setting defines extra links to appear on the
988 ** /sitemap web page as sub-items of the "Home Page" entry before the
989 ** "Documentation Search" entry (if any). For skins that use the /sitemap
990 ** page to construct a hamburger menu dropdown, new entries added here
991 ** will appear on the hamburger menu.
992 **
993 ** This setting should be a TCL list divided into triples. Each
994 ** triple defines a new entry:
995 **
996 ** * The first term is the display name of the /sitemap entry
997 **
998 ** * The second term is a hyperlink to take when a user clicks on the
999 ** entry. Hyperlinks that start with "/" are relative to the
1000 ** repository root.
1001 **
1002 ** * The third term is an argument to the TH1 "capexpr" command.
1003 ** If capexpr evalutes to true, then the entry is shown. If not,
1004 ** the entry is omitted. "*" is always true.
1005 **
1006 ** The default value is blank, meaning no added entries.
1007 */
1008
1009
1010 /*
1011 ** WEBPAGE: setup_config
1012 **
1013 ** The "Admin/Configuration" page. Requires Setup privilege.
@@ -1026,11 +1078,11 @@
1078 @ leading "/".</p>
1079 @ <p>(Property: "index-page")
1080 @ <hr>
1081 @ <p>The main menu for the web interface
1082 @ <p>
1083 @
1084 @ <p>This setting should be a TCL list. Each set of four consecutive
1085 @ values defines a single main menu item:
1086 @ <ol>
1087 @ <li> The first term is text that appears on the menu.
1088 @ <li> The second term is a hyperlink to take when a user clicks on the
@@ -1043,12 +1095,12 @@
1095 @ menu entry. Some skins will classes "desktoponly" and "wideonly"
1096 @ to only show the entries when the web browser screen is wide or
1097 @ very wide, respectively.
1098 @ </ol>
1099 @
1100 @ <p>Some custom skins might not use this property. Whether the property
1101 @ is used or not a choice made by the skin designer. Some skins may add extra
1102 @ choices (such as the hamburger button) to the menu that are not shown
1103 @ on this list. (Property: mainmenu)
1104 @ <p>
1105 if(P("resetMenu")!=0){
1106 db_unset("mainmenu", 0);
1107
+4 -4
--- src/sha1.c
+++ src/sha1.c
@@ -503,14 +503,14 @@
503503
**
504504
** Compute an SHA1 checksum of all files named on the command-line.
505505
** If a file is named "-" then take its content from standard input.
506506
** Options:
507507
**
508
-** -h, --dereference If FILE is a symbolic link, compute the hash
509
-** on the object that the link points to. Normally,
510
-** the hash is over the name of the object that
511
-** the link points to.
508
+** -h|--dereference If FILE is a symbolic link, compute the hash
509
+** on the object that the link points to. Normally,
510
+** the hash is over the name of the object that
511
+** the link points to.
512512
**
513513
** See also: [[md5sum]], [[sha3sum]]
514514
*/
515515
void sha1sum_test(void){
516516
int i;
517517
--- src/sha1.c
+++ src/sha1.c
@@ -503,14 +503,14 @@
503 **
504 ** Compute an SHA1 checksum of all files named on the command-line.
505 ** If a file is named "-" then take its content from standard input.
506 ** Options:
507 **
508 ** -h, --dereference If FILE is a symbolic link, compute the hash
509 ** on the object that the link points to. Normally,
510 ** the hash is over the name of the object that
511 ** the link points to.
512 **
513 ** See also: [[md5sum]], [[sha3sum]]
514 */
515 void sha1sum_test(void){
516 int i;
517
--- src/sha1.c
+++ src/sha1.c
@@ -503,14 +503,14 @@
503 **
504 ** Compute an SHA1 checksum of all files named on the command-line.
505 ** If a file is named "-" then take its content from standard input.
506 ** Options:
507 **
508 ** -h|--dereference If FILE is a symbolic link, compute the hash
509 ** on the object that the link points to. Normally,
510 ** the hash is over the name of the object that
511 ** the link points to.
512 **
513 ** See also: [[md5sum]], [[sha3sum]]
514 */
515 void sha1sum_test(void){
516 int i;
517
+1 -1
--- src/sha3.c
+++ src/sha3.c
@@ -635,11 +635,11 @@
635635
** --256 Compute a SHA3-256 hash (the default)
636636
** --384 Compute a SHA3-384 hash
637637
** --512 Compute a SHA3-512 hash
638638
** --size N An N-bit hash. N must be a multiple of 32 between
639639
** 128 and 512.
640
-** -h, --dereference If FILE is a symbolic link, compute the hash on
640
+** -h|--dereference If FILE is a symbolic link, compute the hash on
641641
** the object pointed to, not on the link itself.
642642
**
643643
** See also: [[md5sum]], [[sha1sum]]
644644
*/
645645
void sha3sum_test(void){
646646
--- src/sha3.c
+++ src/sha3.c
@@ -635,11 +635,11 @@
635 ** --256 Compute a SHA3-256 hash (the default)
636 ** --384 Compute a SHA3-384 hash
637 ** --512 Compute a SHA3-512 hash
638 ** --size N An N-bit hash. N must be a multiple of 32 between
639 ** 128 and 512.
640 ** -h, --dereference If FILE is a symbolic link, compute the hash on
641 ** the object pointed to, not on the link itself.
642 **
643 ** See also: [[md5sum]], [[sha1sum]]
644 */
645 void sha3sum_test(void){
646
--- src/sha3.c
+++ src/sha3.c
@@ -635,11 +635,11 @@
635 ** --256 Compute a SHA3-256 hash (the default)
636 ** --384 Compute a SHA3-384 hash
637 ** --512 Compute a SHA3-512 hash
638 ** --size N An N-bit hash. N must be a multiple of 32 between
639 ** 128 and 512.
640 ** -h|--dereference If FILE is a symbolic link, compute the hash on
641 ** the object pointed to, not on the link itself.
642 **
643 ** See also: [[md5sum]], [[sha1sum]]
644 */
645 void sha3sum_test(void){
646
+205 -96
--- src/shell.c
+++ src/shell.c
@@ -3638,42 +3638,41 @@
36383638
**
36393639
** This file implements a VFS shim that allows an SQLite database to be
36403640
** appended onto the end of some other file, such as an executable.
36413641
**
36423642
** A special record must appear at the end of the file that identifies the
3643
-** file as an appended database and provides an offset to page 1. For
3644
-** best performance page 1 should be located at a disk page boundary, though
3645
-** that is not required.
3643
+** file as an appended database and provides the offset to the first page
3644
+** of the exposed content. (Or, it is the length of the content prefix.)
3645
+** For best performance page 1 should be located at a disk page boundary,
3646
+** though that is not required.
36463647
**
36473648
** When opening a database using this VFS, the connection might treat
3648
-** the file as an ordinary SQLite database, or it might treat is as a
3649
-** database appended onto some other file. Here are the rules:
3650
-**
3651
-** (1) When opening a new empty file, that file is treated as an ordinary
3652
-** database.
3653
-**
3654
-** (2) When opening a file that begins with the standard SQLite prefix
3655
-** string "SQLite format 3", that file is treated as an ordinary
3656
-** database.
3657
-**
3658
-** (3) When opening a file that ends with the appendvfs trailer string
3659
-** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended
3660
-** database.
3649
+** the file as an ordinary SQLite database, or it might treat it as a
3650
+** database appended onto some other file. The decision is made by
3651
+** applying the following rules in order:
3652
+**
3653
+** (1) An empty file is an ordinary database.
3654
+**
3655
+** (2) If the file ends with the appendvfs trailer string
3656
+** "Start-Of-SQLite3-NNNNNNNN" that file is an appended database.
3657
+**
3658
+** (3) If the file begins with the standard SQLite prefix string
3659
+** "SQLite format 3", that file is an ordinary database.
36613660
**
36623661
** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
36633662
** set, then a new database is appended to the already existing file.
36643663
**
36653664
** (5) Otherwise, SQLITE_CANTOPEN is returned.
36663665
**
36673666
** To avoid unnecessary complications with the PENDING_BYTE, the size of
3668
-** the file containing the database is limited to 1GB. This VFS will refuse
3669
-** to read or write past the 1GB mark. This restriction might be lifted in
3670
-** future versions. For now, if you need a large database, then keep the
3671
-** database in a separate file.
3667
+** the file containing the database is limited to 1GB. (1000013824 bytes)
3668
+** This VFS will not read or write past the 1GB mark. This restriction
3669
+** might be lifted in future versions. For now, if you need a larger
3670
+** database, then keep it in a separate file.
36723671
**
3673
-** If the file being opened is not an appended database, then this shim is
3674
-** a pass-through into the default underlying VFS.
3672
+** If the file being opened is a plain database (not an appended one), then
3673
+** this shim is a pass-through into the default underlying VFS. (rule 3)
36753674
**/
36763675
/* #include "sqlite3ext.h" */
36773676
SQLITE_EXTENSION_INIT1
36783677
#include <string.h>
36793678
#include <assert.h>
@@ -3682,22 +3681,30 @@
36823681
**
36833682
** Start-Of-SQLite3-NNNNNNNN
36843683
** 123456789 123456789 12345
36853684
**
36863685
** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
3687
-** the offset to page 1.
3686
+** the offset to page 1, and also the length of the prefix content.
36883687
*/
36893688
#define APND_MARK_PREFIX "Start-Of-SQLite3-"
36903689
#define APND_MARK_PREFIX_SZ 17
3691
-#define APND_MARK_SIZE 25
3690
+#define APND_MARK_FOS_SZ 8
3691
+#define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
36923692
36933693
/*
36943694
** Maximum size of the combined prefix + database + append-mark. This
36953695
** must be less than 0x40000000 to avoid locking issues on Windows.
36963696
*/
36973697
#define APND_MAX_SIZE (65536*15259)
36983698
3699
+/*
3700
+** Size of storage page upon which to align appendvfs portion.
3701
+*/
3702
+#ifndef APND_ROUNDUP_BITS
3703
+#define APND_ROUNDUP_BITS 12
3704
+#endif
3705
+
36993706
/*
37003707
** Forward declaration of objects used by this utility
37013708
*/
37023709
typedef struct sqlite3_vfs ApndVfs;
37033710
typedef struct ApndFile ApndFile;
@@ -3706,15 +3713,43 @@
37063713
** access to randomness, etc.
37073714
*/
37083715
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
37093716
#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
37103717
3711
-/* An open file */
3718
+/* Invariants for an open appendvfs file:
3719
+ * Once an appendvfs file is opened, it will be in one of three states:
3720
+ * State 0: Never written. Underlying file (if any) is unaltered.
3721
+ * State 1: Append mark is persisted, content write is in progress.
3722
+ * State 2: Append mark is persisted, content writes are complete.
3723
+ *
3724
+ * State 0 is persistent in the sense that nothing will have been done
3725
+ * to the underlying file, including any attempt to convert it to an
3726
+ * appendvfs file.
3727
+ *
3728
+ * State 1 is normally transitory. However, if a write operation ends
3729
+ * abnormally (disk full, power loss, process kill, etc.), then State 1
3730
+ * may be persistent on disk with an incomplete content write-out. This
3731
+ * is logically equivalent to an interrupted write to an ordinary file,
3732
+ * where some unknown portion of to-be-written data is persisted while
3733
+ * the remainder is not. Database integrity in such cases is maintained
3734
+ * (or not) by the same measures available for ordinary file access.
3735
+ *
3736
+ * State 2 is persistent under normal circumstances (when there is no
3737
+ * abnormal termination of a write operation such that data provided
3738
+ * to the underlying VFS write method has not yet reached storage.)
3739
+ *
3740
+ * In order to maintain the state invariant, the append mark is written
3741
+ * in advance of content writes where any part of such content would
3742
+ * overwrite an existing (or yet to be written) append mark.
3743
+ */
37123744
struct ApndFile {
3713
- sqlite3_file base; /* IO methods */
3714
- sqlite3_int64 iPgOne; /* File offset to page 1 */
3715
- sqlite3_int64 iMark; /* Start of the append-mark */
3745
+ /* Access to IO methods of the underlying file */
3746
+ sqlite3_file base;
3747
+ /* File offset to beginning of appended content (unchanging) */
3748
+ sqlite3_int64 iPgOne;
3749
+ /* File offset of written append-mark, or -1 if unwritten */
3750
+ sqlite3_int64 iMark;
37163751
};
37173752
37183753
/*
37193754
** Methods for ApndFile
37203755
*/
@@ -3802,12 +3837,10 @@
38023837
apndShmUnmap, /* xShmUnmap */
38033838
apndFetch, /* xFetch */
38043839
apndUnfetch /* xUnfetch */
38053840
};
38063841
3807
-
3808
-
38093842
/*
38103843
** Close an apnd-file.
38113844
*/
38123845
static int apndClose(sqlite3_file *pFile){
38133846
pFile = ORIGFILE(pFile);
@@ -3821,26 +3854,41 @@
38213854
sqlite3_file *pFile,
38223855
void *zBuf,
38233856
int iAmt,
38243857
sqlite_int64 iOfst
38253858
){
3826
- ApndFile *p = (ApndFile *)pFile;
3859
+ ApndFile *paf = (ApndFile *)pFile;
38273860
pFile = ORIGFILE(pFile);
3828
- return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne);
3861
+ return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
38293862
}
38303863
38313864
/*
3832
-** Add the append-mark onto the end of the file.
3865
+** Add the append-mark onto what should become the end of the file.
3866
+* If and only if this succeeds, internal ApndFile.iMark is updated.
3867
+* Parameter iWriteEnd is the appendvfs-relative offset of the new mark.
38333868
*/
3834
-static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){
3835
- int i;
3869
+static int apndWriteMark(
3870
+ ApndFile *paf,
3871
+ sqlite3_file *pFile,
3872
+ sqlite_int64 iWriteEnd
3873
+){
3874
+ sqlite_int64 iPgOne = paf->iPgOne;
38363875
unsigned char a[APND_MARK_SIZE];
3876
+ int i = APND_MARK_FOS_SZ;
3877
+ int rc;
3878
+ assert(pFile == ORIGFILE(paf));
38373879
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
3838
- for(i=0; i<8; i++){
3839
- a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff;
3880
+ while (--i >= 0) {
3881
+ a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
3882
+ iPgOne >>= 8;
38403883
}
3841
- return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark);
3884
+ iWriteEnd += paf->iPgOne;
3885
+ if( SQLITE_OK==(rc = pFile->pMethods->xWrite
3886
+ (pFile, a, APND_MARK_SIZE, iWriteEnd)) ){
3887
+ paf->iMark = iWriteEnd;
3888
+ }
3889
+ return rc;
38423890
}
38433891
38443892
/*
38453893
** Write data to an apnd-file.
38463894
*/
@@ -3848,42 +3896,34 @@
38483896
sqlite3_file *pFile,
38493897
const void *zBuf,
38503898
int iAmt,
38513899
sqlite_int64 iOfst
38523900
){
3853
- int rc;
3854
- ApndFile *p = (ApndFile *)pFile;
3901
+ ApndFile *paf = (ApndFile *)pFile;
3902
+ sqlite_int64 iWriteEnd = iOfst + iAmt;
3903
+ if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
38553904
pFile = ORIGFILE(pFile);
3856
- if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL;
3857
- rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne);
3858
- if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){
3859
- sqlite3_int64 sz = 0;
3860
- rc = pFile->pMethods->xFileSize(pFile, &sz);
3861
- if( rc==SQLITE_OK ){
3862
- p->iMark = sz - APND_MARK_SIZE;
3863
- if( iOfst + iAmt + p->iPgOne > p->iMark ){
3864
- p->iMark = p->iPgOne + iOfst + iAmt;
3865
- rc = apndWriteMark(p, pFile);
3866
- }
3867
- }
3868
- }
3869
- return rc;
3905
+ /* If append-mark is absent or will be overwritten, write it. */
3906
+ if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
3907
+ int rc = apndWriteMark(paf, pFile, iWriteEnd);
3908
+ if( SQLITE_OK!=rc )
3909
+ return rc;
3910
+ }
3911
+ return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
38703912
}
38713913
38723914
/*
38733915
** Truncate an apnd-file.
38743916
*/
38753917
static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
3876
- int rc;
3877
- ApndFile *p = (ApndFile *)pFile;
3918
+ ApndFile *paf = (ApndFile *)pFile;
38783919
pFile = ORIGFILE(pFile);
3879
- rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE);
3880
- if( rc==SQLITE_OK ){
3881
- p->iMark = p->iPgOne+size;
3882
- rc = apndWriteMark(p, pFile);
3883
- }
3884
- return rc;
3920
+ /* The append mark goes out first so truncate failure does not lose it. */
3921
+ if( SQLITE_OK!=apndWriteMark(paf, pFile, size) )
3922
+ return SQLITE_IOERR;
3923
+ /* Truncate underlying file just past append mark */
3924
+ return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
38853925
}
38863926
38873927
/*
38883928
** Sync an apnd-file.
38893929
*/
@@ -3892,20 +3932,16 @@
38923932
return pFile->pMethods->xSync(pFile, flags);
38933933
}
38943934
38953935
/*
38963936
** Return the current file-size of an apnd-file.
3937
+** If the append mark is not yet there, the file-size is 0.
38973938
*/
38983939
static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
3899
- ApndFile *p = (ApndFile *)pFile;
3900
- int rc;
3901
- pFile = ORIGFILE(p);
3902
- rc = pFile->pMethods->xFileSize(pFile, pSize);
3903
- if( rc==SQLITE_OK && p->iPgOne ){
3904
- *pSize -= p->iPgOne + APND_MARK_SIZE;
3905
- }
3906
- return rc;
3940
+ ApndFile *paf = (ApndFile *)pFile;
3941
+ *pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0;
3942
+ return SQLITE_OK;
39073943
}
39083944
39093945
/*
39103946
** Lock an apnd-file.
39113947
*/
@@ -3932,16 +3968,17 @@
39323968
39333969
/*
39343970
** File control method. For custom operations on an apnd-file.
39353971
*/
39363972
static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
3937
- ApndFile *p = (ApndFile *)pFile;
3973
+ ApndFile *paf = (ApndFile *)pFile;
39383974
int rc;
39393975
pFile = ORIGFILE(pFile);
3976
+ if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne;
39403977
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
39413978
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
3942
- *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->iPgOne, *(char**)pArg);
3979
+ *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", paf->iPgOne,*(char**)pArg);
39433980
}
39443981
return rc;
39453982
}
39463983
39473984
/*
@@ -3996,10 +4033,12 @@
39964033
sqlite3_int64 iOfst,
39974034
int iAmt,
39984035
void **pp
39994036
){
40004037
ApndFile *p = (ApndFile *)pFile;
4038
+ if( p->iMark < 0 || iOfst+iAmt > p->iMark)
4039
+ return SQLITE_IOERR; /* Cannot read what is not yet there. */
40014040
pFile = ORIGFILE(pFile);
40024041
return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
40034042
}
40044043
40054044
/* Release a memory-mapped page */
@@ -4007,44 +4046,83 @@
40074046
ApndFile *p = (ApndFile *)pFile;
40084047
pFile = ORIGFILE(pFile);
40094048
return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
40104049
}
40114050
4012
-/*
4013
-** Check to see if the file is an ordinary SQLite database file.
4014
-*/
4015
-static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
4016
- int rc;
4017
- char zHdr[16];
4018
- static const char aSqliteHdr[] = "SQLite format 3";
4019
- if( sz<512 ) return 0;
4020
- rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0);
4021
- if( rc ) return 0;
4022
- return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0;
4023
-}
4024
-
40254051
/*
40264052
** Try to read the append-mark off the end of a file. Return the
4027
-** start of the appended database if the append-mark is present. If
4028
-** there is no append-mark, return -1;
4053
+** start of the appended database if the append-mark is present.
4054
+** If there is no valid append-mark, return -1;
4055
+**
4056
+** An append-mark is only valid if the NNNNNNNN start-of-database offset
4057
+** indicates that the appended database contains at least one page. The
4058
+** start-of-database value must be a multiple of 512.
40294059
*/
40304060
static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
40314061
int rc, i;
40324062
sqlite3_int64 iMark;
4063
+ int msbs = 8 * (APND_MARK_FOS_SZ-1);
40334064
unsigned char a[APND_MARK_SIZE];
40344065
4035
- if( sz<=APND_MARK_SIZE ) return -1;
4066
+ if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1;
40364067
rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
40374068
if( rc ) return -1;
40384069
if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
4039
- iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ]&0x7f))<<56;
4040
- for(i=1; i<8; i++){
4041
- iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i);
4070
+ iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0x7f)) << msbs;
4071
+ for(i=1; i<8; i++){
4072
+ msbs -= 8;
4073
+ iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs;
40424074
}
4075
+ if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1;
4076
+ if( iMark & 0x1ff ) return -1;
40434077
return iMark;
40444078
}
40454079
4080
+static const char apvfsSqliteHdr[] = "SQLite format 3";
4081
+/*
4082
+** Check to see if the file is an appendvfs SQLite database file.
4083
+** Return true iff it is such. Parameter sz is the file's size.
4084
+*/
4085
+static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
4086
+ int rc;
4087
+ char zHdr[16];
4088
+ sqlite3_int64 iMark = apndReadMark(sz, pFile);
4089
+ if( iMark>=0 ){
4090
+ /* If file has right end-marker, the expected odd size, and the
4091
+ * SQLite DB type marker where the end-marker puts it, then it
4092
+ * is an appendvfs database (to be treated as such.)
4093
+ */
4094
+ rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
4095
+ if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4096
+ && (sz & 0x1ff)== APND_MARK_SIZE && sz>=512+APND_MARK_SIZE )
4097
+ return 1; /* It's an appendvfs database */
4098
+ }
4099
+ return 0;
4100
+}
4101
+
4102
+/*
4103
+** Check to see if the file is an ordinary SQLite database file.
4104
+** Return true iff so. Parameter sz is the file's size.
4105
+*/
4106
+static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
4107
+ char zHdr[16];
4108
+ if( apndIsAppendvfsDatabase(sz, pFile) /* rule 2 */
4109
+ || (sz & 0x1ff) != 0
4110
+ || SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0)
4111
+ || memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))!=0
4112
+ ){
4113
+ return 0;
4114
+ }else{
4115
+ return 1;
4116
+ }
4117
+}
4118
+
4119
+/* Round-up used to get appendvfs portion to begin at a page boundary. */
4120
+#define APND_ALIGN_MASK(nbits) ((1<<nbits)-1)
4121
+#define APND_START_ROUNDUP(fsz, nbits) \
4122
+ ( ((fsz)+APND_ALIGN_MASK(nbits)) & ~(sqlite3_int64)APND_ALIGN_MASK(nbits) )
4123
+
40464124
/*
40474125
** Open an apnd file handle.
40484126
*/
40494127
static int apndOpen(
40504128
sqlite3_vfs *pVfs,
@@ -4058,10 +4136,11 @@
40584136
sqlite3_vfs *pSubVfs;
40594137
int rc;
40604138
sqlite3_int64 sz;
40614139
pSubVfs = ORIGVFS(pVfs);
40624140
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
4141
+ /* The appendvfs is not to be used for transient or temporary databases. */
40634142
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
40644143
}
40654144
p = (ApndFile*)pFile;
40664145
memset(p, 0, sizeof(*p));
40674146
pSubFile = ORIGFILE(pFile);
@@ -4075,31 +4154,46 @@
40754154
}
40764155
if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
40774156
memmove(pFile, pSubFile, pSubVfs->szOsFile);
40784157
return SQLITE_OK;
40794158
}
4080
- p->iMark = 0;
4159
+ /* Record that append mark has not been written until seen otherwise. */
4160
+ p->iMark = -1;
40814161
p->iPgOne = apndReadMark(sz, pFile);
4082
- if( p->iPgOne>0 ){
4162
+ if( p->iPgOne>=0 ){
4163
+ /* Append mark was found, infer its offset */
4164
+ p->iMark = sz - p->iPgOne - APND_MARK_SIZE;
40834165
return SQLITE_OK;
40844166
}
40854167
if( (flags & SQLITE_OPEN_CREATE)==0 ){
40864168
pSubFile->pMethods->xClose(pSubFile);
40874169
rc = SQLITE_CANTOPEN;
40884170
}
4089
- p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff;
4171
+ /* Round newly added appendvfs location to #define'd page boundary.
4172
+ * Note that nothing has yet been written to the underlying file.
4173
+ * The append mark will be written along with first content write.
4174
+ * Until then, the p->iMark value indicates it is not yet written.
4175
+ */
4176
+ p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS);
40904177
apnd_open_done:
40914178
if( rc ) pFile->pMethods = 0;
40924179
return rc;
40934180
}
40944181
40954182
/*
4096
-** All other VFS methods are pass-thrus.
4183
+** Delete an apnd file.
4184
+** For an appendvfs, this could mean delete the appendvfs portion,
4185
+** leaving the appendee as it was before it gained an appendvfs.
4186
+** For now, this code deletes the underlying file too.
40974187
*/
40984188
static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
40994189
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
41004190
}
4191
+
4192
+/*
4193
+** All other VFS methods are pass-thrus.
4194
+*/
41014195
static int apndAccess(
41024196
sqlite3_vfs *pVfs,
41034197
const char *zPath,
41044198
int flags,
41054199
int *pResOut
@@ -5202,10 +5296,18 @@
52025296
sqlite3_int64 m, e, a;
52035297
double r;
52045298
int isNeg = 0;
52055299
m = sqlite3_value_int64(argv[0]);
52065300
e = sqlite3_value_int64(argv[1]);
5301
+
5302
+ /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
5303
+ if( e>10000 ){
5304
+ e = 10000;
5305
+ }else if( e<-10000 ){
5306
+ e = -10000;
5307
+ }
5308
+
52075309
if( m<0 ){
52085310
isNeg = 1;
52095311
m = -m;
52105312
if( m<0 ) return;
52115313
}else if( m==0 && e>-1000 && e<1000 ){
@@ -19080,18 +19182,24 @@
1908019182
raw_printf(stderr, "Usage: .read FILE\n");
1908119183
rc = 1;
1908219184
goto meta_command_exit;
1908319185
}
1908419186
if( azArg[1][0]=='|' ){
19187
+#ifdef SQLITE_OMIT_POPEN
19188
+ raw_printf(stderr, "Error: pipes are not supported in this OS\n");
19189
+ rc = 1;
19190
+ p->out = stdout;
19191
+#else
1908519192
p->in = popen(azArg[1]+1, "r");
1908619193
if( p->in==0 ){
1908719194
utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
1908819195
rc = 1;
1908919196
}else{
1909019197
rc = process_input(p);
1909119198
pclose(p->in);
1909219199
}
19200
+#endif
1909319201
}else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){
1909419202
utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
1909519203
rc = 1;
1909619204
}else{
1909719205
rc = process_input(p);
@@ -20865,11 +20973,12 @@
2086520973
}
2086620974
return argv[i];
2086720975
}
2086820976
2086920977
#ifndef SQLITE_SHELL_IS_UTF8
20870
-# if (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER)
20978
+# if (defined(_WIN32) || defined(WIN32)) \
20979
+ && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
2087120980
# define SQLITE_SHELL_IS_UTF8 (0)
2087220981
# else
2087320982
# define SQLITE_SHELL_IS_UTF8 (1)
2087420983
# endif
2087520984
#endif
2087620985
--- src/shell.c
+++ src/shell.c
@@ -3638,42 +3638,41 @@
3638 **
3639 ** This file implements a VFS shim that allows an SQLite database to be
3640 ** appended onto the end of some other file, such as an executable.
3641 **
3642 ** A special record must appear at the end of the file that identifies the
3643 ** file as an appended database and provides an offset to page 1. For
3644 ** best performance page 1 should be located at a disk page boundary, though
3645 ** that is not required.
 
3646 **
3647 ** When opening a database using this VFS, the connection might treat
3648 ** the file as an ordinary SQLite database, or it might treat is as a
3649 ** database appended onto some other file. Here are the rules:
3650 **
3651 ** (1) When opening a new empty file, that file is treated as an ordinary
3652 ** database.
3653 **
3654 ** (2) When opening a file that begins with the standard SQLite prefix
3655 ** string "SQLite format 3", that file is treated as an ordinary
3656 ** database.
3657 **
3658 ** (3) When opening a file that ends with the appendvfs trailer string
3659 ** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended
3660 ** database.
3661 **
3662 ** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
3663 ** set, then a new database is appended to the already existing file.
3664 **
3665 ** (5) Otherwise, SQLITE_CANTOPEN is returned.
3666 **
3667 ** To avoid unnecessary complications with the PENDING_BYTE, the size of
3668 ** the file containing the database is limited to 1GB. This VFS will refuse
3669 ** to read or write past the 1GB mark. This restriction might be lifted in
3670 ** future versions. For now, if you need a large database, then keep the
3671 ** database in a separate file.
3672 **
3673 ** If the file being opened is not an appended database, then this shim is
3674 ** a pass-through into the default underlying VFS.
3675 **/
3676 /* #include "sqlite3ext.h" */
3677 SQLITE_EXTENSION_INIT1
3678 #include <string.h>
3679 #include <assert.h>
@@ -3682,22 +3681,30 @@
3682 **
3683 ** Start-Of-SQLite3-NNNNNNNN
3684 ** 123456789 123456789 12345
3685 **
3686 ** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
3687 ** the offset to page 1.
3688 */
3689 #define APND_MARK_PREFIX "Start-Of-SQLite3-"
3690 #define APND_MARK_PREFIX_SZ 17
3691 #define APND_MARK_SIZE 25
 
3692
3693 /*
3694 ** Maximum size of the combined prefix + database + append-mark. This
3695 ** must be less than 0x40000000 to avoid locking issues on Windows.
3696 */
3697 #define APND_MAX_SIZE (65536*15259)
3698
 
 
 
 
 
 
 
3699 /*
3700 ** Forward declaration of objects used by this utility
3701 */
3702 typedef struct sqlite3_vfs ApndVfs;
3703 typedef struct ApndFile ApndFile;
@@ -3706,15 +3713,43 @@
3706 ** access to randomness, etc.
3707 */
3708 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
3709 #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
3710
3711 /* An open file */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3712 struct ApndFile {
3713 sqlite3_file base; /* IO methods */
3714 sqlite3_int64 iPgOne; /* File offset to page 1 */
3715 sqlite3_int64 iMark; /* Start of the append-mark */
 
 
 
3716 };
3717
3718 /*
3719 ** Methods for ApndFile
3720 */
@@ -3802,12 +3837,10 @@
3802 apndShmUnmap, /* xShmUnmap */
3803 apndFetch, /* xFetch */
3804 apndUnfetch /* xUnfetch */
3805 };
3806
3807
3808
3809 /*
3810 ** Close an apnd-file.
3811 */
3812 static int apndClose(sqlite3_file *pFile){
3813 pFile = ORIGFILE(pFile);
@@ -3821,26 +3854,41 @@
3821 sqlite3_file *pFile,
3822 void *zBuf,
3823 int iAmt,
3824 sqlite_int64 iOfst
3825 ){
3826 ApndFile *p = (ApndFile *)pFile;
3827 pFile = ORIGFILE(pFile);
3828 return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne);
3829 }
3830
3831 /*
3832 ** Add the append-mark onto the end of the file.
 
 
3833 */
3834 static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){
3835 int i;
 
 
 
 
3836 unsigned char a[APND_MARK_SIZE];
 
 
 
3837 memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
3838 for(i=0; i<8; i++){
3839 a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff;
 
3840 }
3841 return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark);
 
 
 
 
 
3842 }
3843
3844 /*
3845 ** Write data to an apnd-file.
3846 */
@@ -3848,42 +3896,34 @@
3848 sqlite3_file *pFile,
3849 const void *zBuf,
3850 int iAmt,
3851 sqlite_int64 iOfst
3852 ){
3853 int rc;
3854 ApndFile *p = (ApndFile *)pFile;
 
3855 pFile = ORIGFILE(pFile);
3856 if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL;
3857 rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne);
3858 if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){
3859 sqlite3_int64 sz = 0;
3860 rc = pFile->pMethods->xFileSize(pFile, &sz);
3861 if( rc==SQLITE_OK ){
3862 p->iMark = sz - APND_MARK_SIZE;
3863 if( iOfst + iAmt + p->iPgOne > p->iMark ){
3864 p->iMark = p->iPgOne + iOfst + iAmt;
3865 rc = apndWriteMark(p, pFile);
3866 }
3867 }
3868 }
3869 return rc;
3870 }
3871
3872 /*
3873 ** Truncate an apnd-file.
3874 */
3875 static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
3876 int rc;
3877 ApndFile *p = (ApndFile *)pFile;
3878 pFile = ORIGFILE(pFile);
3879 rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE);
3880 if( rc==SQLITE_OK ){
3881 p->iMark = p->iPgOne+size;
3882 rc = apndWriteMark(p, pFile);
3883 }
3884 return rc;
3885 }
3886
3887 /*
3888 ** Sync an apnd-file.
3889 */
@@ -3892,20 +3932,16 @@
3892 return pFile->pMethods->xSync(pFile, flags);
3893 }
3894
3895 /*
3896 ** Return the current file-size of an apnd-file.
 
3897 */
3898 static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
3899 ApndFile *p = (ApndFile *)pFile;
3900 int rc;
3901 pFile = ORIGFILE(p);
3902 rc = pFile->pMethods->xFileSize(pFile, pSize);
3903 if( rc==SQLITE_OK && p->iPgOne ){
3904 *pSize -= p->iPgOne + APND_MARK_SIZE;
3905 }
3906 return rc;
3907 }
3908
3909 /*
3910 ** Lock an apnd-file.
3911 */
@@ -3932,16 +3968,17 @@
3932
3933 /*
3934 ** File control method. For custom operations on an apnd-file.
3935 */
3936 static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
3937 ApndFile *p = (ApndFile *)pFile;
3938 int rc;
3939 pFile = ORIGFILE(pFile);
 
3940 rc = pFile->pMethods->xFileControl(pFile, op, pArg);
3941 if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
3942 *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->iPgOne, *(char**)pArg);
3943 }
3944 return rc;
3945 }
3946
3947 /*
@@ -3996,10 +4033,12 @@
3996 sqlite3_int64 iOfst,
3997 int iAmt,
3998 void **pp
3999 ){
4000 ApndFile *p = (ApndFile *)pFile;
 
 
4001 pFile = ORIGFILE(pFile);
4002 return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
4003 }
4004
4005 /* Release a memory-mapped page */
@@ -4007,44 +4046,83 @@
4007 ApndFile *p = (ApndFile *)pFile;
4008 pFile = ORIGFILE(pFile);
4009 return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
4010 }
4011
4012 /*
4013 ** Check to see if the file is an ordinary SQLite database file.
4014 */
4015 static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
4016 int rc;
4017 char zHdr[16];
4018 static const char aSqliteHdr[] = "SQLite format 3";
4019 if( sz<512 ) return 0;
4020 rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0);
4021 if( rc ) return 0;
4022 return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0;
4023 }
4024
4025 /*
4026 ** Try to read the append-mark off the end of a file. Return the
4027 ** start of the appended database if the append-mark is present. If
4028 ** there is no append-mark, return -1;
 
 
 
 
4029 */
4030 static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
4031 int rc, i;
4032 sqlite3_int64 iMark;
 
4033 unsigned char a[APND_MARK_SIZE];
4034
4035 if( sz<=APND_MARK_SIZE ) return -1;
4036 rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
4037 if( rc ) return -1;
4038 if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
4039 iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ]&0x7f))<<56;
4040 for(i=1; i<8; i++){
4041 iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i);
 
4042 }
 
 
4043 return iMark;
4044 }
4045
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4046 /*
4047 ** Open an apnd file handle.
4048 */
4049 static int apndOpen(
4050 sqlite3_vfs *pVfs,
@@ -4058,10 +4136,11 @@
4058 sqlite3_vfs *pSubVfs;
4059 int rc;
4060 sqlite3_int64 sz;
4061 pSubVfs = ORIGVFS(pVfs);
4062 if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
 
4063 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
4064 }
4065 p = (ApndFile*)pFile;
4066 memset(p, 0, sizeof(*p));
4067 pSubFile = ORIGFILE(pFile);
@@ -4075,31 +4154,46 @@
4075 }
4076 if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
4077 memmove(pFile, pSubFile, pSubVfs->szOsFile);
4078 return SQLITE_OK;
4079 }
4080 p->iMark = 0;
 
4081 p->iPgOne = apndReadMark(sz, pFile);
4082 if( p->iPgOne>0 ){
 
 
4083 return SQLITE_OK;
4084 }
4085 if( (flags & SQLITE_OPEN_CREATE)==0 ){
4086 pSubFile->pMethods->xClose(pSubFile);
4087 rc = SQLITE_CANTOPEN;
4088 }
4089 p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff;
 
 
 
 
 
4090 apnd_open_done:
4091 if( rc ) pFile->pMethods = 0;
4092 return rc;
4093 }
4094
4095 /*
4096 ** All other VFS methods are pass-thrus.
 
 
 
4097 */
4098 static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
4099 return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
4100 }
 
 
 
 
4101 static int apndAccess(
4102 sqlite3_vfs *pVfs,
4103 const char *zPath,
4104 int flags,
4105 int *pResOut
@@ -5202,10 +5296,18 @@
5202 sqlite3_int64 m, e, a;
5203 double r;
5204 int isNeg = 0;
5205 m = sqlite3_value_int64(argv[0]);
5206 e = sqlite3_value_int64(argv[1]);
 
 
 
 
 
 
 
 
5207 if( m<0 ){
5208 isNeg = 1;
5209 m = -m;
5210 if( m<0 ) return;
5211 }else if( m==0 && e>-1000 && e<1000 ){
@@ -19080,18 +19182,24 @@
19080 raw_printf(stderr, "Usage: .read FILE\n");
19081 rc = 1;
19082 goto meta_command_exit;
19083 }
19084 if( azArg[1][0]=='|' ){
 
 
 
 
 
19085 p->in = popen(azArg[1]+1, "r");
19086 if( p->in==0 ){
19087 utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
19088 rc = 1;
19089 }else{
19090 rc = process_input(p);
19091 pclose(p->in);
19092 }
 
19093 }else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){
19094 utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
19095 rc = 1;
19096 }else{
19097 rc = process_input(p);
@@ -20865,11 +20973,12 @@
20865 }
20866 return argv[i];
20867 }
20868
20869 #ifndef SQLITE_SHELL_IS_UTF8
20870 # if (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER)
 
20871 # define SQLITE_SHELL_IS_UTF8 (0)
20872 # else
20873 # define SQLITE_SHELL_IS_UTF8 (1)
20874 # endif
20875 #endif
20876
--- src/shell.c
+++ src/shell.c
@@ -3638,42 +3638,41 @@
3638 **
3639 ** This file implements a VFS shim that allows an SQLite database to be
3640 ** appended onto the end of some other file, such as an executable.
3641 **
3642 ** A special record must appear at the end of the file that identifies the
3643 ** file as an appended database and provides the offset to the first page
3644 ** of the exposed content. (Or, it is the length of the content prefix.)
3645 ** For best performance page 1 should be located at a disk page boundary,
3646 ** though that is not required.
3647 **
3648 ** When opening a database using this VFS, the connection might treat
3649 ** the file as an ordinary SQLite database, or it might treat it as a
3650 ** database appended onto some other file. The decision is made by
3651 ** applying the following rules in order:
3652 **
3653 ** (1) An empty file is an ordinary database.
3654 **
3655 ** (2) If the file ends with the appendvfs trailer string
3656 ** "Start-Of-SQLite3-NNNNNNNN" that file is an appended database.
3657 **
3658 ** (3) If the file begins with the standard SQLite prefix string
3659 ** "SQLite format 3", that file is an ordinary database.
 
 
3660 **
3661 ** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
3662 ** set, then a new database is appended to the already existing file.
3663 **
3664 ** (5) Otherwise, SQLITE_CANTOPEN is returned.
3665 **
3666 ** To avoid unnecessary complications with the PENDING_BYTE, the size of
3667 ** the file containing the database is limited to 1GB. (1000013824 bytes)
3668 ** This VFS will not read or write past the 1GB mark. This restriction
3669 ** might be lifted in future versions. For now, if you need a larger
3670 ** database, then keep it in a separate file.
3671 **
3672 ** If the file being opened is a plain database (not an appended one), then
3673 ** this shim is a pass-through into the default underlying VFS. (rule 3)
3674 **/
3675 /* #include "sqlite3ext.h" */
3676 SQLITE_EXTENSION_INIT1
3677 #include <string.h>
3678 #include <assert.h>
@@ -3682,22 +3681,30 @@
3681 **
3682 ** Start-Of-SQLite3-NNNNNNNN
3683 ** 123456789 123456789 12345
3684 **
3685 ** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
3686 ** the offset to page 1, and also the length of the prefix content.
3687 */
3688 #define APND_MARK_PREFIX "Start-Of-SQLite3-"
3689 #define APND_MARK_PREFIX_SZ 17
3690 #define APND_MARK_FOS_SZ 8
3691 #define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
3692
3693 /*
3694 ** Maximum size of the combined prefix + database + append-mark. This
3695 ** must be less than 0x40000000 to avoid locking issues on Windows.
3696 */
3697 #define APND_MAX_SIZE (65536*15259)
3698
3699 /*
3700 ** Size of storage page upon which to align appendvfs portion.
3701 */
3702 #ifndef APND_ROUNDUP_BITS
3703 #define APND_ROUNDUP_BITS 12
3704 #endif
3705
3706 /*
3707 ** Forward declaration of objects used by this utility
3708 */
3709 typedef struct sqlite3_vfs ApndVfs;
3710 typedef struct ApndFile ApndFile;
@@ -3706,15 +3713,43 @@
3713 ** access to randomness, etc.
3714 */
3715 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
3716 #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
3717
3718 /* Invariants for an open appendvfs file:
3719 * Once an appendvfs file is opened, it will be in one of three states:
3720 * State 0: Never written. Underlying file (if any) is unaltered.
3721 * State 1: Append mark is persisted, content write is in progress.
3722 * State 2: Append mark is persisted, content writes are complete.
3723 *
3724 * State 0 is persistent in the sense that nothing will have been done
3725 * to the underlying file, including any attempt to convert it to an
3726 * appendvfs file.
3727 *
3728 * State 1 is normally transitory. However, if a write operation ends
3729 * abnormally (disk full, power loss, process kill, etc.), then State 1
3730 * may be persistent on disk with an incomplete content write-out. This
3731 * is logically equivalent to an interrupted write to an ordinary file,
3732 * where some unknown portion of to-be-written data is persisted while
3733 * the remainder is not. Database integrity in such cases is maintained
3734 * (or not) by the same measures available for ordinary file access.
3735 *
3736 * State 2 is persistent under normal circumstances (when there is no
3737 * abnormal termination of a write operation such that data provided
3738 * to the underlying VFS write method has not yet reached storage.)
3739 *
3740 * In order to maintain the state invariant, the append mark is written
3741 * in advance of content writes where any part of such content would
3742 * overwrite an existing (or yet to be written) append mark.
3743 */
3744 struct ApndFile {
3745 /* Access to IO methods of the underlying file */
3746 sqlite3_file base;
3747 /* File offset to beginning of appended content (unchanging) */
3748 sqlite3_int64 iPgOne;
3749 /* File offset of written append-mark, or -1 if unwritten */
3750 sqlite3_int64 iMark;
3751 };
3752
3753 /*
3754 ** Methods for ApndFile
3755 */
@@ -3802,12 +3837,10 @@
3837 apndShmUnmap, /* xShmUnmap */
3838 apndFetch, /* xFetch */
3839 apndUnfetch /* xUnfetch */
3840 };
3841
 
 
3842 /*
3843 ** Close an apnd-file.
3844 */
3845 static int apndClose(sqlite3_file *pFile){
3846 pFile = ORIGFILE(pFile);
@@ -3821,26 +3854,41 @@
3854 sqlite3_file *pFile,
3855 void *zBuf,
3856 int iAmt,
3857 sqlite_int64 iOfst
3858 ){
3859 ApndFile *paf = (ApndFile *)pFile;
3860 pFile = ORIGFILE(pFile);
3861 return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
3862 }
3863
3864 /*
3865 ** Add the append-mark onto what should become the end of the file.
3866 * If and only if this succeeds, internal ApndFile.iMark is updated.
3867 * Parameter iWriteEnd is the appendvfs-relative offset of the new mark.
3868 */
3869 static int apndWriteMark(
3870 ApndFile *paf,
3871 sqlite3_file *pFile,
3872 sqlite_int64 iWriteEnd
3873 ){
3874 sqlite_int64 iPgOne = paf->iPgOne;
3875 unsigned char a[APND_MARK_SIZE];
3876 int i = APND_MARK_FOS_SZ;
3877 int rc;
3878 assert(pFile == ORIGFILE(paf));
3879 memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
3880 while (--i >= 0) {
3881 a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
3882 iPgOne >>= 8;
3883 }
3884 iWriteEnd += paf->iPgOne;
3885 if( SQLITE_OK==(rc = pFile->pMethods->xWrite
3886 (pFile, a, APND_MARK_SIZE, iWriteEnd)) ){
3887 paf->iMark = iWriteEnd;
3888 }
3889 return rc;
3890 }
3891
3892 /*
3893 ** Write data to an apnd-file.
3894 */
@@ -3848,42 +3896,34 @@
3896 sqlite3_file *pFile,
3897 const void *zBuf,
3898 int iAmt,
3899 sqlite_int64 iOfst
3900 ){
3901 ApndFile *paf = (ApndFile *)pFile;
3902 sqlite_int64 iWriteEnd = iOfst + iAmt;
3903 if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
3904 pFile = ORIGFILE(pFile);
3905 /* If append-mark is absent or will be overwritten, write it. */
3906 if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
3907 int rc = apndWriteMark(paf, pFile, iWriteEnd);
3908 if( SQLITE_OK!=rc )
3909 return rc;
3910 }
3911 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
 
 
 
 
 
 
 
3912 }
3913
3914 /*
3915 ** Truncate an apnd-file.
3916 */
3917 static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
3918 ApndFile *paf = (ApndFile *)pFile;
 
3919 pFile = ORIGFILE(pFile);
3920 /* The append mark goes out first so truncate failure does not lose it. */
3921 if( SQLITE_OK!=apndWriteMark(paf, pFile, size) )
3922 return SQLITE_IOERR;
3923 /* Truncate underlying file just past append mark */
3924 return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
 
3925 }
3926
3927 /*
3928 ** Sync an apnd-file.
3929 */
@@ -3892,20 +3932,16 @@
3932 return pFile->pMethods->xSync(pFile, flags);
3933 }
3934
3935 /*
3936 ** Return the current file-size of an apnd-file.
3937 ** If the append mark is not yet there, the file-size is 0.
3938 */
3939 static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
3940 ApndFile *paf = (ApndFile *)pFile;
3941 *pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0;
3942 return SQLITE_OK;
 
 
 
 
 
3943 }
3944
3945 /*
3946 ** Lock an apnd-file.
3947 */
@@ -3932,16 +3968,17 @@
3968
3969 /*
3970 ** File control method. For custom operations on an apnd-file.
3971 */
3972 static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
3973 ApndFile *paf = (ApndFile *)pFile;
3974 int rc;
3975 pFile = ORIGFILE(pFile);
3976 if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne;
3977 rc = pFile->pMethods->xFileControl(pFile, op, pArg);
3978 if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
3979 *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", paf->iPgOne,*(char**)pArg);
3980 }
3981 return rc;
3982 }
3983
3984 /*
@@ -3996,10 +4033,12 @@
4033 sqlite3_int64 iOfst,
4034 int iAmt,
4035 void **pp
4036 ){
4037 ApndFile *p = (ApndFile *)pFile;
4038 if( p->iMark < 0 || iOfst+iAmt > p->iMark)
4039 return SQLITE_IOERR; /* Cannot read what is not yet there. */
4040 pFile = ORIGFILE(pFile);
4041 return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
4042 }
4043
4044 /* Release a memory-mapped page */
@@ -4007,44 +4046,83 @@
4046 ApndFile *p = (ApndFile *)pFile;
4047 pFile = ORIGFILE(pFile);
4048 return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
4049 }
4050
 
 
 
 
 
 
 
 
 
 
 
 
 
4051 /*
4052 ** Try to read the append-mark off the end of a file. Return the
4053 ** start of the appended database if the append-mark is present.
4054 ** If there is no valid append-mark, return -1;
4055 **
4056 ** An append-mark is only valid if the NNNNNNNN start-of-database offset
4057 ** indicates that the appended database contains at least one page. The
4058 ** start-of-database value must be a multiple of 512.
4059 */
4060 static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
4061 int rc, i;
4062 sqlite3_int64 iMark;
4063 int msbs = 8 * (APND_MARK_FOS_SZ-1);
4064 unsigned char a[APND_MARK_SIZE];
4065
4066 if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1;
4067 rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
4068 if( rc ) return -1;
4069 if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
4070 iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0x7f)) << msbs;
4071 for(i=1; i<8; i++){
4072 msbs -= 8;
4073 iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs;
4074 }
4075 if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1;
4076 if( iMark & 0x1ff ) return -1;
4077 return iMark;
4078 }
4079
4080 static const char apvfsSqliteHdr[] = "SQLite format 3";
4081 /*
4082 ** Check to see if the file is an appendvfs SQLite database file.
4083 ** Return true iff it is such. Parameter sz is the file's size.
4084 */
4085 static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
4086 int rc;
4087 char zHdr[16];
4088 sqlite3_int64 iMark = apndReadMark(sz, pFile);
4089 if( iMark>=0 ){
4090 /* If file has right end-marker, the expected odd size, and the
4091 * SQLite DB type marker where the end-marker puts it, then it
4092 * is an appendvfs database (to be treated as such.)
4093 */
4094 rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
4095 if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4096 && (sz & 0x1ff)== APND_MARK_SIZE && sz>=512+APND_MARK_SIZE )
4097 return 1; /* It's an appendvfs database */
4098 }
4099 return 0;
4100 }
4101
4102 /*
4103 ** Check to see if the file is an ordinary SQLite database file.
4104 ** Return true iff so. Parameter sz is the file's size.
4105 */
4106 static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
4107 char zHdr[16];
4108 if( apndIsAppendvfsDatabase(sz, pFile) /* rule 2 */
4109 || (sz & 0x1ff) != 0
4110 || SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0)
4111 || memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))!=0
4112 ){
4113 return 0;
4114 }else{
4115 return 1;
4116 }
4117 }
4118
4119 /* Round-up used to get appendvfs portion to begin at a page boundary. */
4120 #define APND_ALIGN_MASK(nbits) ((1<<nbits)-1)
4121 #define APND_START_ROUNDUP(fsz, nbits) \
4122 ( ((fsz)+APND_ALIGN_MASK(nbits)) & ~(sqlite3_int64)APND_ALIGN_MASK(nbits) )
4123
4124 /*
4125 ** Open an apnd file handle.
4126 */
4127 static int apndOpen(
4128 sqlite3_vfs *pVfs,
@@ -4058,10 +4136,11 @@
4136 sqlite3_vfs *pSubVfs;
4137 int rc;
4138 sqlite3_int64 sz;
4139 pSubVfs = ORIGVFS(pVfs);
4140 if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
4141 /* The appendvfs is not to be used for transient or temporary databases. */
4142 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
4143 }
4144 p = (ApndFile*)pFile;
4145 memset(p, 0, sizeof(*p));
4146 pSubFile = ORIGFILE(pFile);
@@ -4075,31 +4154,46 @@
4154 }
4155 if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
4156 memmove(pFile, pSubFile, pSubVfs->szOsFile);
4157 return SQLITE_OK;
4158 }
4159 /* Record that append mark has not been written until seen otherwise. */
4160 p->iMark = -1;
4161 p->iPgOne = apndReadMark(sz, pFile);
4162 if( p->iPgOne>=0 ){
4163 /* Append mark was found, infer its offset */
4164 p->iMark = sz - p->iPgOne - APND_MARK_SIZE;
4165 return SQLITE_OK;
4166 }
4167 if( (flags & SQLITE_OPEN_CREATE)==0 ){
4168 pSubFile->pMethods->xClose(pSubFile);
4169 rc = SQLITE_CANTOPEN;
4170 }
4171 /* Round newly added appendvfs location to #define'd page boundary.
4172 * Note that nothing has yet been written to the underlying file.
4173 * The append mark will be written along with first content write.
4174 * Until then, the p->iMark value indicates it is not yet written.
4175 */
4176 p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS);
4177 apnd_open_done:
4178 if( rc ) pFile->pMethods = 0;
4179 return rc;
4180 }
4181
4182 /*
4183 ** Delete an apnd file.
4184 ** For an appendvfs, this could mean delete the appendvfs portion,
4185 ** leaving the appendee as it was before it gained an appendvfs.
4186 ** For now, this code deletes the underlying file too.
4187 */
4188 static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
4189 return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
4190 }
4191
4192 /*
4193 ** All other VFS methods are pass-thrus.
4194 */
4195 static int apndAccess(
4196 sqlite3_vfs *pVfs,
4197 const char *zPath,
4198 int flags,
4199 int *pResOut
@@ -5202,10 +5296,18 @@
5296 sqlite3_int64 m, e, a;
5297 double r;
5298 int isNeg = 0;
5299 m = sqlite3_value_int64(argv[0]);
5300 e = sqlite3_value_int64(argv[1]);
5301
5302 /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
5303 if( e>10000 ){
5304 e = 10000;
5305 }else if( e<-10000 ){
5306 e = -10000;
5307 }
5308
5309 if( m<0 ){
5310 isNeg = 1;
5311 m = -m;
5312 if( m<0 ) return;
5313 }else if( m==0 && e>-1000 && e<1000 ){
@@ -19080,18 +19182,24 @@
19182 raw_printf(stderr, "Usage: .read FILE\n");
19183 rc = 1;
19184 goto meta_command_exit;
19185 }
19186 if( azArg[1][0]=='|' ){
19187 #ifdef SQLITE_OMIT_POPEN
19188 raw_printf(stderr, "Error: pipes are not supported in this OS\n");
19189 rc = 1;
19190 p->out = stdout;
19191 #else
19192 p->in = popen(azArg[1]+1, "r");
19193 if( p->in==0 ){
19194 utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
19195 rc = 1;
19196 }else{
19197 rc = process_input(p);
19198 pclose(p->in);
19199 }
19200 #endif
19201 }else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){
19202 utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
19203 rc = 1;
19204 }else{
19205 rc = process_input(p);
@@ -20865,11 +20973,12 @@
20973 }
20974 return argv[i];
20975 }
20976
20977 #ifndef SQLITE_SHELL_IS_UTF8
20978 # if (defined(_WIN32) || defined(WIN32)) \
20979 && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
20980 # define SQLITE_SHELL_IS_UTF8 (0)
20981 # else
20982 # define SQLITE_SHELL_IS_UTF8 (1)
20983 # endif
20984 #endif
20985
+54 -6
--- src/skins.c
+++ src/skins.c
@@ -104,21 +104,61 @@
104104
**
105105
** If the alternative skin name contains one or more '/' characters, then
106106
** it is assumed to be a directory on disk that holds override css.txt,
107107
** footer.txt, and header.txt. This mode can be used for interactive
108108
** development of new skins.
109
+**
110
+** The 2nd parameter is a ranking of how important this alternative
111
+** skin declaration is, and lower values trump higher ones. If a call
112
+** to this function passes a higher-valued rank than a previous call,
113
+** the subsequent call becomes a no-op. Only calls with the same or
114
+** lower rank (i.e. higher priority) will overwrite a previous
115
+** setting. This approach is used because the CGI/server-time
116
+** initialization happens in an order which is incompatible with our
117
+** preferred ranking, making it otherwise more invasive to tell the
118
+** internals "the --skin flag ranks higher than a URL parameter" (the
119
+** former gets initialized before both URL parameters and the /draft
120
+** path determination).
121
+**
122
+** The rankings were initially defined in
123
+** https://fossil-scm.org/forum/forumpost/caf8c9a8bb
124
+** and are:
125
+**
126
+** 0) A skin name matching the glob draft[1-9] trumps everything else.
127
+**
128
+** 1) The --skin flag or skin: CGI config setting.
129
+**
130
+** 2) The "skin" display setting cookie or URL argument, in that
131
+** order. If the "skin" URL argument is provided and refers to a legal
132
+** skin then that will update the display cookie. If the skin name is
133
+** illegal it is silently ignored.
134
+**
135
+** 3) Skin properties from the CONFIG db table
136
+**
137
+** 4) Default skin.
138
+**
139
+** As a special case, a NULL or empty name resets zAltSkinDir and
140
+** pAltSkin to 0 to indicate that the current config-side skin should
141
+** be used (rank 3, above), then returns 0.
109142
*/
110
-char *skin_use_alternative(const char *zName){
143
+char *skin_use_alternative(const char *zName, int rank){
144
+ static int currentRank = 5;
111145
int i;
112146
Blob err = BLOB_INITIALIZER;
113
- if( strchr(zName, '/')!=0 ){
147
+ if(rank > currentRank) return 0;
148
+ if( zName && 1==rank && strchr(zName, '/')!=0 ){
114149
zAltSkinDir = fossil_strdup(zName);
115150
return 0;
116151
}
117
- if( sqlite3_strglob("draft[1-9]", zName)==0 ){
152
+ if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){
118153
skin_use_draft(zName[5] - '0');
119154
return 0;
155
+ }
156
+ if(!zName || !*zName){
157
+ pAltSkin = 0;
158
+ zAltSkinDir = 0;
159
+ return 0;
120160
}
121161
for(i=0; i<count(aBuiltinSkin); i++){
122162
if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
123163
pAltSkin = &aBuiltinSkin[i];
124164
return 0;
@@ -137,11 +177,11 @@
137177
** call fossil_fatal() if an unknown skin is specified.
138178
*/
139179
void skin_override(void){
140180
const char *zSkin = find_option("skin",0,1);
141181
if( zSkin ){
142
- char *zErr = skin_use_alternative(zSkin);
182
+ char *zErr = skin_use_alternative(zSkin, 1);
143183
if( zErr ) fossil_fatal("%s", zErr);
144184
}
145185
}
146186
147187
/*
@@ -1154,24 +1194,32 @@
11541194
}
11551195
fossil_free(zPattern);
11561196
}
11571197
login_check_credentials();
11581198
style_header("Skins");
1199
+ if(zAltSkinDir && zAltSkinDir[0]){
1200
+ @ <p class="warning">Warning: this fossil instance was started with
1201
+ @ a hard-coded skin value which trumps any option selected below.
1202
+ @ A skins selected below will be recorded in your prefere cookie
1203
+ @ but will not be used until/unless the site administrator
1204
+ @ configures the site to run without a forced hard-coded skin.
1205
+ @ </p>
1206
+ }
11591207
@ <p>The following skins are available for this repository:</p>
11601208
@ <ul>
11611209
if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){
11621210
@ <li> Standard skin for this repository &larr; <i>Currently in use</i>
11631211
}else{
1164
- @ <li> %z(href("%s/skins",zBase))Standard skin for this repository</a>
1212
+ @ <li> %z(href("%R/skins?skin="))Standard skin for this repository</a>
11651213
}
11661214
for(i=0; i<count(aBuiltinSkin); i++){
11671215
if( pAltSkin==&aBuiltinSkin[i] ){
11681216
@ <li> %h(aBuiltinSkin[i].zDesc) &larr; <i>Currently in use</i>
11691217
}else{
1170
- char *zUrl = href("%s/skn_%s/skins", zBase, aBuiltinSkin[i].zLabel);
1218
+ char *zUrl = href("%R/skins?skin=%T", aBuiltinSkin[i].zLabel);
11711219
@ <li> %z(zUrl)%h(aBuiltinSkin[i].zDesc)</a>
11721220
}
11731221
}
11741222
@ </ul>
11751223
style_finish_page();
11761224
fossil_free(zBase);
11771225
}
11781226
--- src/skins.c
+++ src/skins.c
@@ -104,21 +104,61 @@
104 **
105 ** If the alternative skin name contains one or more '/' characters, then
106 ** it is assumed to be a directory on disk that holds override css.txt,
107 ** footer.txt, and header.txt. This mode can be used for interactive
108 ** development of new skins.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109 */
110 char *skin_use_alternative(const char *zName){
 
111 int i;
112 Blob err = BLOB_INITIALIZER;
113 if( strchr(zName, '/')!=0 ){
 
114 zAltSkinDir = fossil_strdup(zName);
115 return 0;
116 }
117 if( sqlite3_strglob("draft[1-9]", zName)==0 ){
118 skin_use_draft(zName[5] - '0');
119 return 0;
 
 
 
 
 
120 }
121 for(i=0; i<count(aBuiltinSkin); i++){
122 if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
123 pAltSkin = &aBuiltinSkin[i];
124 return 0;
@@ -137,11 +177,11 @@
137 ** call fossil_fatal() if an unknown skin is specified.
138 */
139 void skin_override(void){
140 const char *zSkin = find_option("skin",0,1);
141 if( zSkin ){
142 char *zErr = skin_use_alternative(zSkin);
143 if( zErr ) fossil_fatal("%s", zErr);
144 }
145 }
146
147 /*
@@ -1154,24 +1194,32 @@
1154 }
1155 fossil_free(zPattern);
1156 }
1157 login_check_credentials();
1158 style_header("Skins");
 
 
 
 
 
 
 
 
1159 @ <p>The following skins are available for this repository:</p>
1160 @ <ul>
1161 if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){
1162 @ <li> Standard skin for this repository &larr; <i>Currently in use</i>
1163 }else{
1164 @ <li> %z(href("%s/skins",zBase))Standard skin for this repository</a>
1165 }
1166 for(i=0; i<count(aBuiltinSkin); i++){
1167 if( pAltSkin==&aBuiltinSkin[i] ){
1168 @ <li> %h(aBuiltinSkin[i].zDesc) &larr; <i>Currently in use</i>
1169 }else{
1170 char *zUrl = href("%s/skn_%s/skins", zBase, aBuiltinSkin[i].zLabel);
1171 @ <li> %z(zUrl)%h(aBuiltinSkin[i].zDesc)</a>
1172 }
1173 }
1174 @ </ul>
1175 style_finish_page();
1176 fossil_free(zBase);
1177 }
1178
--- src/skins.c
+++ src/skins.c
@@ -104,21 +104,61 @@
104 **
105 ** If the alternative skin name contains one or more '/' characters, then
106 ** it is assumed to be a directory on disk that holds override css.txt,
107 ** footer.txt, and header.txt. This mode can be used for interactive
108 ** development of new skins.
109 **
110 ** The 2nd parameter is a ranking of how important this alternative
111 ** skin declaration is, and lower values trump higher ones. If a call
112 ** to this function passes a higher-valued rank than a previous call,
113 ** the subsequent call becomes a no-op. Only calls with the same or
114 ** lower rank (i.e. higher priority) will overwrite a previous
115 ** setting. This approach is used because the CGI/server-time
116 ** initialization happens in an order which is incompatible with our
117 ** preferred ranking, making it otherwise more invasive to tell the
118 ** internals "the --skin flag ranks higher than a URL parameter" (the
119 ** former gets initialized before both URL parameters and the /draft
120 ** path determination).
121 **
122 ** The rankings were initially defined in
123 ** https://fossil-scm.org/forum/forumpost/caf8c9a8bb
124 ** and are:
125 **
126 ** 0) A skin name matching the glob draft[1-9] trumps everything else.
127 **
128 ** 1) The --skin flag or skin: CGI config setting.
129 **
130 ** 2) The "skin" display setting cookie or URL argument, in that
131 ** order. If the "skin" URL argument is provided and refers to a legal
132 ** skin then that will update the display cookie. If the skin name is
133 ** illegal it is silently ignored.
134 **
135 ** 3) Skin properties from the CONFIG db table
136 **
137 ** 4) Default skin.
138 **
139 ** As a special case, a NULL or empty name resets zAltSkinDir and
140 ** pAltSkin to 0 to indicate that the current config-side skin should
141 ** be used (rank 3, above), then returns 0.
142 */
143 char *skin_use_alternative(const char *zName, int rank){
144 static int currentRank = 5;
145 int i;
146 Blob err = BLOB_INITIALIZER;
147 if(rank > currentRank) return 0;
148 if( zName && 1==rank && strchr(zName, '/')!=0 ){
149 zAltSkinDir = fossil_strdup(zName);
150 return 0;
151 }
152 if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){
153 skin_use_draft(zName[5] - '0');
154 return 0;
155 }
156 if(!zName || !*zName){
157 pAltSkin = 0;
158 zAltSkinDir = 0;
159 return 0;
160 }
161 for(i=0; i<count(aBuiltinSkin); i++){
162 if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
163 pAltSkin = &aBuiltinSkin[i];
164 return 0;
@@ -137,11 +177,11 @@
177 ** call fossil_fatal() if an unknown skin is specified.
178 */
179 void skin_override(void){
180 const char *zSkin = find_option("skin",0,1);
181 if( zSkin ){
182 char *zErr = skin_use_alternative(zSkin, 1);
183 if( zErr ) fossil_fatal("%s", zErr);
184 }
185 }
186
187 /*
@@ -1154,24 +1194,32 @@
1194 }
1195 fossil_free(zPattern);
1196 }
1197 login_check_credentials();
1198 style_header("Skins");
1199 if(zAltSkinDir && zAltSkinDir[0]){
1200 @ <p class="warning">Warning: this fossil instance was started with
1201 @ a hard-coded skin value which trumps any option selected below.
1202 @ A skins selected below will be recorded in your prefere cookie
1203 @ but will not be used until/unless the site administrator
1204 @ configures the site to run without a forced hard-coded skin.
1205 @ </p>
1206 }
1207 @ <p>The following skins are available for this repository:</p>
1208 @ <ul>
1209 if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){
1210 @ <li> Standard skin for this repository &larr; <i>Currently in use</i>
1211 }else{
1212 @ <li> %z(href("%R/skins?skin="))Standard skin for this repository</a>
1213 }
1214 for(i=0; i<count(aBuiltinSkin); i++){
1215 if( pAltSkin==&aBuiltinSkin[i] ){
1216 @ <li> %h(aBuiltinSkin[i].zDesc) &larr; <i>Currently in use</i>
1217 }else{
1218 char *zUrl = href("%R/skins?skin=%T", aBuiltinSkin[i].zLabel);
1219 @ <li> %z(zUrl)%h(aBuiltinSkin[i].zDesc)</a>
1220 }
1221 }
1222 @ </ul>
1223 style_finish_page();
1224 fossil_free(zBase);
1225 }
1226
--- src/sqlcompattest.c
+++ src/sqlcompattest.c
@@ -13,15 +13,18 @@
1313
** [email protected]
1414
** http://www.hwaci.com/drh/
1515
**
1616
*******************************************************************************
1717
**
18
-** This file is NOT part of the Fossil executable
18
+** This file is NOT part of the Fossil executable. It is called from
19
+** auto.def in the autosetup system.
1920
**
2021
** This file contains a test program used by ../configure with the
2122
** the --disable-internal-sqlite option to determine whether or
2223
** not the system SQLite library is sufficient to support Fossil.
24
+**
25
+** This must be compiled with -D MINIMUM_SQLITE_VERSION set in auto.def.
2326
**
2427
** It is preferred to statically link Fossil with the sqlite3.c source
2528
** file that is part of the source tree and not use any SQLite shared
2629
** library that is included with the system. But some packagers do not
2730
** like to do this. Hence, we provide the option to link Fossil against
@@ -38,23 +41,39 @@
3841
** lacks some capability that Fossil uses. A message on stdout describes
3942
** the missing feature.
4043
*/
4144
#include "sqlite3.h"
4245
#include <stdio.h>
46
+#include <string.h>
4347
4448
int main(int argc, char **argv){
49
+
50
+#if !defined(MINIMUM_SQLITE_VERSION)
51
+#error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def"
52
+#endif
53
+
54
+#define QUOTE(VAL) #VAL
55
+#define STR(MACRO_VAL) QUOTE(MACRO_VAL)
56
+
57
+ char zMinimumVersionNumber[8]="nn.nn.nn";
58
+ strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION),sizeof(zMinimumVersionNumber));
59
+
60
+ long major, minor, release, version;
61
+ sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release);
62
+ version=(major*1000000)+(minor*1000)+release;
63
+
4564
int i;
4665
static const char *zRequiredOpts[] = {
4766
"ENABLE_FTS4", /* Required for repository search */
4867
"ENABLE_JSON1", /* Required for the check-in locking protocol */
4968
"ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
5069
};
5170
5271
/* Check minimum SQLite version number */
53
- if( sqlite3_libversion_number()<3033000 ){
54
- printf("found SQLite version %s but need 3.33.0 or later\n",
55
- sqlite3_libversion());
72
+ if( sqlite3_libversion_number()<version ){
73
+ printf("found system SQLite version %s but need %s or later, consider removing --disable-internal-sqlite\n",
74
+ sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION));
5675
return 1;
5776
}
5877
5978
for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
6079
if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
6180
--- src/sqlcompattest.c
+++ src/sqlcompattest.c
@@ -13,15 +13,18 @@
13 ** [email protected]
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file is NOT part of the Fossil executable
 
19 **
20 ** This file contains a test program used by ../configure with the
21 ** the --disable-internal-sqlite option to determine whether or
22 ** not the system SQLite library is sufficient to support Fossil.
 
 
23 **
24 ** It is preferred to statically link Fossil with the sqlite3.c source
25 ** file that is part of the source tree and not use any SQLite shared
26 ** library that is included with the system. But some packagers do not
27 ** like to do this. Hence, we provide the option to link Fossil against
@@ -38,23 +41,39 @@
38 ** lacks some capability that Fossil uses. A message on stdout describes
39 ** the missing feature.
40 */
41 #include "sqlite3.h"
42 #include <stdio.h>
 
43
44 int main(int argc, char **argv){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45 int i;
46 static const char *zRequiredOpts[] = {
47 "ENABLE_FTS4", /* Required for repository search */
48 "ENABLE_JSON1", /* Required for the check-in locking protocol */
49 "ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
50 };
51
52 /* Check minimum SQLite version number */
53 if( sqlite3_libversion_number()<3033000 ){
54 printf("found SQLite version %s but need 3.33.0 or later\n",
55 sqlite3_libversion());
56 return 1;
57 }
58
59 for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
60 if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
61
--- src/sqlcompattest.c
+++ src/sqlcompattest.c
@@ -13,15 +13,18 @@
13 ** [email protected]
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file is NOT part of the Fossil executable. It is called from
19 ** auto.def in the autosetup system.
20 **
21 ** This file contains a test program used by ../configure with the
22 ** the --disable-internal-sqlite option to determine whether or
23 ** not the system SQLite library is sufficient to support Fossil.
24 **
25 ** This must be compiled with -D MINIMUM_SQLITE_VERSION set in auto.def.
26 **
27 ** It is preferred to statically link Fossil with the sqlite3.c source
28 ** file that is part of the source tree and not use any SQLite shared
29 ** library that is included with the system. But some packagers do not
30 ** like to do this. Hence, we provide the option to link Fossil against
@@ -38,23 +41,39 @@
41 ** lacks some capability that Fossil uses. A message on stdout describes
42 ** the missing feature.
43 */
44 #include "sqlite3.h"
45 #include <stdio.h>
46 #include <string.h>
47
48 int main(int argc, char **argv){
49
50 #if !defined(MINIMUM_SQLITE_VERSION)
51 #error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def"
52 #endif
53
54 #define QUOTE(VAL) #VAL
55 #define STR(MACRO_VAL) QUOTE(MACRO_VAL)
56
57 char zMinimumVersionNumber[8]="nn.nn.nn";
58 strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION),sizeof(zMinimumVersionNumber));
59
60 long major, minor, release, version;
61 sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release);
62 version=(major*1000000)+(minor*1000)+release;
63
64 int i;
65 static const char *zRequiredOpts[] = {
66 "ENABLE_FTS4", /* Required for repository search */
67 "ENABLE_JSON1", /* Required for the check-in locking protocol */
68 "ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
69 };
70
71 /* Check minimum SQLite version number */
72 if( sqlite3_libversion_number()<version ){
73 printf("found system SQLite version %s but need %s or later, consider removing --disable-internal-sqlite\n",
74 sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION));
75 return 1;
76 }
77
78 for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
79 if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
80
+346 -146
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1186,11 +1186,11 @@
11861186
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
11871187
** [sqlite_version()] and [sqlite_source_id()].
11881188
*/
11891189
#define SQLITE_VERSION "3.35.0"
11901190
#define SQLITE_VERSION_NUMBER 3035000
1191
-#define SQLITE_SOURCE_ID "2021-02-22 16:42:09 b5a0778cc5a98a864bea72670f83262da940aceb91fa4cdf46ec097337a3alt1"
1191
+#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
11921192
11931193
/*
11941194
** CAPI3REF: Run-Time Library Version Numbers
11951195
** KEYWORDS: sqlite3_version sqlite3_sourceid
11961196
**
@@ -3193,11 +3193,17 @@
31933193
** The first argument is an integer which is 0 to disable views,
31943194
** positive to enable views or negative to leave the setting unchanged.
31953195
** The second parameter is a pointer to an integer into which
31963196
** is written 0 or 1 to indicate whether views are disabled or enabled
31973197
** following this call. The second parameter may be a NULL pointer, in
3198
-** which case the view setting is not reported back. </dd>
3198
+** which case the view setting is not reported back.
3199
+**
3200
+** <p>Originally this option disabled all views. ^(However, since
3201
+** SQLite version 3.35.0, TEMP views are still allowed even if
3202
+** this option is off. So, in other words, this option now only disables
3203
+** views in the main database schema or in the schemas of ATTACH-ed
3204
+** databases.)^ </dd>
31993205
**
32003206
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
32013207
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
32023208
** <dd> ^This option is used to enable or disable the
32033209
** [fts3_tokenizer()] function which is part of the
@@ -11617,22 +11623,27 @@
1161711623
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
1161811624
** created by [sqlite3changeset_start()]. In the latter case, the most recent
1161911625
** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
1162011626
** is not the case, this function returns [SQLITE_MISUSE].
1162111627
**
11622
-** If argument pzTab is not NULL, then *pzTab is set to point to a
11623
-** nul-terminated utf-8 encoded string containing the name of the table
11624
-** affected by the current change. The buffer remains valid until either
11625
-** sqlite3changeset_next() is called on the iterator or until the
11626
-** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
11627
-** set to the number of columns in the table affected by the change. If
11628
-** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
11628
+** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
11629
+** outputs are set through these pointers:
11630
+**
11631
+** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
11632
+** depending on the type of change that the iterator currently points to;
11633
+**
11634
+** *pnCol is set to the number of columns in the table affected by the change; and
11635
+**
11636
+** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
11637
+** the name of the table affected by the current change. The buffer remains
11638
+** valid until either sqlite3changeset_next() is called on the iterator
11639
+** or until the conflict-handler function returns.
11640
+**
11641
+** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
1162911642
** is an indirect change, or false (0) otherwise. See the documentation for
1163011643
** [sqlite3session_indirect()] for a description of direct and indirect
11631
-** changes. Finally, if pOp is not NULL, then *pOp is set to one of
11632
-** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
11633
-** type of change that the iterator currently points to.
11644
+** changes.
1163411645
**
1163511646
** If no error occurs, SQLITE_OK is returned. If an error does occur, an
1163611647
** SQLite error code is returned. The values of the output variables may not
1163711648
** be trusted in this case.
1163811649
*/
@@ -15368,11 +15379,11 @@
1536815379
1536915380
/* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
1537015381
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
1537115382
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
1537215383
#define BTREE_APPEND 0x08 /* Insert is likely an append */
15373
-#define BTREE_PREFORMAT 0x80 /* Insert is likely an append */
15384
+#define BTREE_PREFORMAT 0x80 /* Inserted data is a preformated cell */
1537415385
1537515386
/* An instance of the BtreePayload object describes the content of a single
1537615387
** entry in either an index or table btree.
1537715388
**
1537815389
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
@@ -16942,10 +16953,15 @@
1694216953
#define SQLITE_TRACE_LEGACY 0
1694316954
#define SQLITE_TRACE_XPROFILE 0
1694416955
#endif /* SQLITE_OMIT_DEPRECATED */
1694516956
#define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */
1694616957
16958
+/*
16959
+** Maximum number of sqlite3.aDb[] entries. This is the number of attached
16960
+** databases plus 2 for "main" and "temp".
16961
+*/
16962
+#define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2)
1694716963
1694816964
/*
1694916965
** Each database connection is an instance of the following structure.
1695016966
*/
1695116967
struct sqlite3 {
@@ -18665,10 +18681,11 @@
1866518681
#define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */
1866618682
#define SF_View 0x0200000 /* SELECT statement is a view */
1866718683
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
1866818684
#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
1866918685
#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
18686
+#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
1867018687
1867118688
/*
1867218689
** The results of a SELECT can be distributed in several ways, as defined
1867318690
** by one of the following macros. The "SRT" prefix means "SELECT Result
1867418691
** Type".
@@ -20255,10 +20272,11 @@
2025520272
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
2025620273
SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
2025720274
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
2025820275
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
2025920276
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
20277
+SQLITE_PRIVATE const char *sqlite3SelectOpName(int);
2026020278
SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*);
2026120279
2026220280
#ifdef SQLITE_DEBUG
2026320281
SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
2026420282
#endif
@@ -29329,11 +29347,11 @@
2932929347
setStrAccumError(p, SQLITE_TOOBIG);
2933029348
return p->nAlloc - p->nChar - 1;
2933129349
}else{
2933229350
char *zOld = isMalloced(p) ? p->zText : 0;
2933329351
i64 szNew = p->nChar;
29334
- szNew += N + 1;
29352
+ szNew += (sqlite3_int64)N + 1;
2933529353
if( szNew+p->nChar<=p->mxAlloc ){
2933629354
/* Force exponential buffer size growth as long as it does not overflow,
2933729355
** to avoid having to call this routine too often */
2933829356
szNew += p->nChar;
2933929357
}
@@ -50636,10 +50654,11 @@
5063650654
#endif
5063750655
p->page.pBuf = pPg;
5063850656
p->page.pExtra = &p[1];
5063950657
p->isBulkLocal = 0;
5064050658
p->isAnchor = 0;
50659
+ p->pLruPrev = 0; /* Initializing this saves a valgrind error */
5064150660
}
5064250661
(*pCache->pnPurgeable)++;
5064350662
return p;
5064450663
}
5064550664
@@ -52554,10 +52573,11 @@
5255452573
i64 iOffset; /* Starting offset in main journal */
5255552574
i64 iHdrOffset; /* See above */
5255652575
Bitvec *pInSavepoint; /* Set of pages in this savepoint */
5255752576
Pgno nOrig; /* Original number of pages in file */
5255852577
Pgno iSubRec; /* Index of first record in sub-journal */
52578
+ int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */
5255952579
#ifndef SQLITE_OMIT_WAL
5256052580
u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
5256152581
#endif
5256252582
};
5256352583
@@ -53189,10 +53209,13 @@
5318953209
Pgno pgno = pPg->pgno;
5319053210
int i;
5319153211
for(i=0; i<pPager->nSavepoint; i++){
5319253212
p = &pPager->aSavepoint[i];
5319353213
if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
53214
+ for(i=i+1; i<pPager->nSavepoint; i++){
53215
+ pPager->aSavepoint[i].bTruncateOnRelease = 0;
53216
+ }
5319453217
return 1;
5319553218
}
5319653219
}
5319753220
return 0;
5319853221
}
@@ -58967,10 +58990,11 @@
5896758990
}else{
5896858991
aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
5896958992
}
5897058993
aNew[ii].iSubRec = pPager->nSubRec;
5897158994
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
58995
+ aNew[ii].bTruncateOnRelease = 1;
5897258996
if( !aNew[ii].pInSavepoint ){
5897358997
return SQLITE_NOMEM_BKPT;
5897458998
}
5897558999
if( pagerUseWal(pPager) ){
5897659000
sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
@@ -59048,17 +59072,19 @@
5904859072
pPager->nSavepoint = nNew;
5904959073
5905059074
/* If this is a release of the outermost savepoint, truncate
5905159075
** the sub-journal to zero bytes in size. */
5905259076
if( op==SAVEPOINT_RELEASE ){
59053
- if( nNew==0 && isOpen(pPager->sjfd) ){
59077
+ PagerSavepoint *pRel = &pPager->aSavepoint[nNew];
59078
+ if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){
5905459079
/* Only truncate if it is an in-memory sub-journal. */
5905559080
if( sqlite3JournalIsInMemory(pPager->sjfd) ){
59056
- rc = sqlite3OsTruncate(pPager->sjfd, 0);
59081
+ i64 sz = (pPager->pageSize+4)*pRel->iSubRec;
59082
+ rc = sqlite3OsTruncate(pPager->sjfd, sz);
5905759083
assert( rc==SQLITE_OK );
5905859084
}
59059
- pPager->nSubRec = 0;
59085
+ pPager->nSubRec = pRel->iSubRec;
5906059086
}
5906159087
}
5906259088
/* Else this is a rollback operation, playback the specified savepoint.
5906359089
** If this is a temp-file, it is possible that the journal file has
5906459090
** not yet been opened. In this case there have been no changes to
@@ -72558,11 +72584,13 @@
7255872584
}else{
7255972585
pRight = findCell(pParent, i+nxDiv-pParent->nOverflow);
7256072586
}
7256172587
pgno = get4byte(pRight);
7256272588
while( 1 ){
72563
- rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
72589
+ if( rc==SQLITE_OK ){
72590
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
72591
+ }
7256472592
if( rc ){
7256572593
memset(apOld, 0, (i+1)*sizeof(MemPage*));
7256672594
goto balance_cleanup;
7256772595
}
7256872596
if( apOld[i]->nFree<0 ){
@@ -72597,16 +72625,14 @@
7259772625
** buffer. It will be copied out again as soon as the aSpace[] buffer
7259872626
** is allocated. */
7259972627
if( pBt->btsFlags & BTS_FAST_SECURE ){
7260072628
int iOff;
7260172629
72630
+ /* If the following if() condition is not true, the db is corrupted.
72631
+ ** The call to dropCell() below will detect this. */
7260272632
iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
72603
- if( (iOff+szNew[i])>(int)pBt->usableSize ){
72604
- rc = SQLITE_CORRUPT_BKPT;
72605
- memset(apOld, 0, (i+1)*sizeof(MemPage*));
72606
- goto balance_cleanup;
72607
- }else{
72633
+ if( (iOff+szNew[i])<=(int)pBt->usableSize ){
7260872634
memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
7260972635
apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
7261072636
}
7261172637
}
7261272638
dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc);
@@ -87386,11 +87412,11 @@
8738687412
case OP_ChngCntRow: {
8738787413
assert( pOp->p2==1 );
8738887414
if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
8738987415
goto abort_due_to_error;
8739087416
}
87391
- /* Fall through to the next case, OP_String */
87417
+ /* Fall through to the next case, OP_ResultRow */
8739287418
/* no break */ deliberate_fall_through
8739387419
}
8739487420
8739587421
/* Opcode: ResultRow P1 P2 * * *
8739687422
** Synopsis: output=r[P1@P2]
@@ -97963,11 +97989,10 @@
9796397989
struct MemJournal {
9796497990
const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
9796597991
int nChunkSize; /* In-memory chunk-size */
9796697992
9796797993
int nSpill; /* Bytes of data before flushing */
97968
- int nSize; /* Bytes of data currently in memory */
9796997994
FileChunk *pFirst; /* Head of in-memory chunk-list */
9797097995
FilePoint endpoint; /* Pointer to the end of the file */
9797197996
FilePoint readpoint; /* Pointer to the end of the last xRead() */
9797297997
9797397998
int flags; /* xOpen flags */
@@ -98024,18 +98049,17 @@
9802498049
}
9802598050
9802698051
/*
9802798052
** Free the list of FileChunk structures headed at MemJournal.pFirst.
9802898053
*/
98029
-static void memjrnlFreeChunks(MemJournal *p){
98054
+static void memjrnlFreeChunks(FileChunk *pFirst){
9803098055
FileChunk *pIter;
9803198056
FileChunk *pNext;
98032
- for(pIter=p->pFirst; pIter; pIter=pNext){
98057
+ for(pIter=pFirst; pIter; pIter=pNext){
9803398058
pNext = pIter->pNext;
9803498059
sqlite3_free(pIter);
9803598060
}
98036
- p->pFirst = 0;
9803798061
}
9803898062
9803998063
/*
9804098064
** Flush the contents of memory to a real file on disk.
9804198065
*/
@@ -98058,11 +98082,11 @@
9805898082
if( rc ) break;
9805998083
iOff += nChunk;
9806098084
}
9806198085
if( rc==SQLITE_OK ){
9806298086
/* No error has occurred. Free the in-memory buffers. */
98063
- memjrnlFreeChunks(&copy);
98087
+ memjrnlFreeChunks(copy.pFirst);
9806498088
}
9806598089
}
9806698090
if( rc!=SQLITE_OK ){
9806798091
/* If an error occurred while creating or writing to the file, restore
9806898092
** the original before returning. This way, SQLite uses the in-memory
@@ -98141,43 +98165,50 @@
9814198165
memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
9814298166
zWrite += iSpace;
9814398167
nWrite -= iSpace;
9814498168
p->endpoint.iOffset += iSpace;
9814598169
}
98146
- p->nSize = iAmt + iOfst;
9814798170
}
9814898171
}
9814998172
9815098173
return SQLITE_OK;
9815198174
}
9815298175
9815398176
/*
98154
-** Truncate the file.
98155
-**
98156
-** If the journal file is already on disk, truncate it there. Or, if it
98157
-** is still in main memory but is being truncated to zero bytes in size,
98158
-** ignore
98177
+** Truncate the in-memory file.
9815998178
*/
9816098179
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
9816198180
MemJournal *p = (MemJournal *)pJfd;
98162
- if( ALWAYS(size==0) ){
98163
- memjrnlFreeChunks(p);
98164
- p->nSize = 0;
98165
- p->endpoint.pChunk = 0;
98166
- p->endpoint.iOffset = 0;
98167
- p->readpoint.pChunk = 0;
98168
- p->readpoint.iOffset = 0;
98169
- }
98181
+ FileChunk *pIter = 0;
98182
+
98183
+ if( size==0 ){
98184
+ memjrnlFreeChunks(p->pFirst);
98185
+ p->pFirst = 0;
98186
+ }else{
98187
+ i64 iOff = p->nChunkSize;
98188
+ for(pIter=p->pFirst; ALWAYS(pIter) && iOff<=size; pIter=pIter->pNext){
98189
+ iOff += p->nChunkSize;
98190
+ }
98191
+ if( ALWAYS(pIter) ){
98192
+ memjrnlFreeChunks(pIter->pNext);
98193
+ pIter->pNext = 0;
98194
+ }
98195
+ }
98196
+
98197
+ p->endpoint.pChunk = pIter;
98198
+ p->endpoint.iOffset = size;
98199
+ p->readpoint.pChunk = 0;
98200
+ p->readpoint.iOffset = 0;
9817098201
return SQLITE_OK;
9817198202
}
9817298203
9817398204
/*
9817498205
** Close the file.
9817598206
*/
9817698207
static int memjrnlClose(sqlite3_file *pJfd){
9817798208
MemJournal *p = (MemJournal *)pJfd;
98178
- memjrnlFreeChunks(p);
98209
+ memjrnlFreeChunks(p->pFirst);
9817998210
return SQLITE_OK;
9818098211
}
9818198212
9818298213
/*
9818398214
** Sync the file.
@@ -98992,10 +99023,11 @@
9899299023
/* IMP: R-51414-32910 */
9899399024
iCol = -1;
9899499025
}
9899599026
if( iCol<pTab->nCol ){
9899699027
cnt++;
99028
+ pMatch = 0;
9899799029
#ifndef SQLITE_OMIT_UPSERT
9899899030
if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){
9899999031
testcase( iCol==(-1) );
9900099032
if( IN_RENAME_OBJECT ){
9900199033
pExpr->iColumn = iCol;
@@ -99010,12 +99042,12 @@
9901099042
#endif /* SQLITE_OMIT_UPSERT */
9901199043
{
9901299044
pExpr->y.pTab = pTab;
9901399045
if( pParse->bReturning ){
9901499046
eNewExprOp = TK_REGISTER;
99015
- pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable
99016
- + iCol + 1;
99047
+ pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable +
99048
+ sqlite3TableColumnToStorage(pTab, iCol) + 1;
9901799049
}else{
9901899050
pExpr->iColumn = (i16)iCol;
9901999051
eNewExprOp = TK_TRIGGER;
9902099052
#ifndef SQLITE_OMIT_TRIGGER
9902199053
if( iCol<0 ){
@@ -99211,15 +99243,17 @@
9921199243
pExpr->op = eNewExprOp;
9921299244
ExprSetProperty(pExpr, EP_Leaf);
9921399245
lookupname_end:
9921499246
if( cnt==1 ){
9921599247
assert( pNC!=0 );
99248
+#ifndef SQLITE_OMIT_AUTHORIZATION
9921699249
if( pParse->db->xAuth
9921799250
&& (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
9921899251
){
9921999252
sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
9922099253
}
99254
+#endif
9922199255
/* Increment the nRef value on all name contexts from TopNC up to
9922299256
** the point where the name matched. */
9922399257
for(;;){
9922499258
assert( pTopNC!=0 );
9922599259
pTopNC->nRef++;
@@ -99359,10 +99393,51 @@
9935999393
pExpr->iTable = pItem->iCursor;
9936099394
pExpr->iColumn--;
9936199395
pExpr->affExpr = SQLITE_AFF_INTEGER;
9936299396
break;
9936399397
}
99398
+
99399
+ /* An optimization: Attempt to convert
99400
+ **
99401
+ ** "expr IS NOT NULL" --> "TRUE"
99402
+ ** "expr IS NULL" --> "FALSE"
99403
+ **
99404
+ ** if we can prove that "expr" is never NULL. Call this the
99405
+ ** "NOT NULL strength reduction optimization".
99406
+ **
99407
+ ** If this optimization occurs, also restore the NameContext ref-counts
99408
+ ** to the state they where in before the "column" LHS expression was
99409
+ ** resolved. This prevents "column" from being counted as having been
99410
+ ** referenced, which might prevent a SELECT from being erroneously
99411
+ ** marked as correlated.
99412
+ */
99413
+ case TK_NOTNULL:
99414
+ case TK_ISNULL: {
99415
+ int anRef[8];
99416
+ NameContext *p;
99417
+ int i;
99418
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
99419
+ anRef[i] = p->nRef;
99420
+ }
99421
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
99422
+ if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
99423
+ if( pExpr->op==TK_NOTNULL ){
99424
+ pExpr->u.zToken = "true";
99425
+ ExprSetProperty(pExpr, EP_IsTrue);
99426
+ }else{
99427
+ pExpr->u.zToken = "false";
99428
+ ExprSetProperty(pExpr, EP_IsFalse);
99429
+ }
99430
+ pExpr->op = TK_TRUEFALSE;
99431
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
99432
+ p->nRef = anRef[i];
99433
+ }
99434
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
99435
+ pExpr->pLeft = 0;
99436
+ }
99437
+ return WRC_Prune;
99438
+ }
9936499439
9936599440
/* A column name: ID
9936699441
** Or table name and column name: ID.ID
9936799442
** Or a database, table and column: ID.ID.ID
9936899443
**
@@ -99587,10 +99662,11 @@
9958799662
if( pWin ){
9958899663
Select *pSel = pNC->pWinSelect;
9958999664
assert( pWin==pExpr->y.pWin );
9959099665
if( IN_RENAME_OBJECT==0 ){
9959199666
sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef);
99667
+ if( pParse->db->mallocFailed ) break;
9959299668
}
9959399669
sqlite3WalkExprList(pWalker, pWin->pPartition);
9959499670
sqlite3WalkExprList(pWalker, pWin->pOrderBy);
9959599671
sqlite3WalkExpr(pWalker, pWin->pFilter);
9959699672
sqlite3WindowLink(pSel, pWin);
@@ -99661,11 +99737,11 @@
9966199737
case TK_ISNOT: {
9966299738
Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
9966399739
assert( !ExprHasProperty(pExpr, EP_Reduced) );
9966499740
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
9966599741
** and "x IS NOT FALSE". */
99666
- if( pRight && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){
99742
+ if( ALWAYS(pRight) && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){
9966799743
int rc = resolveExprStep(pWalker, pRight);
9966899744
if( rc==WRC_Abort ) return WRC_Abort;
9966999745
if( pRight->op==TK_TRUEFALSE ){
9967099746
pExpr->op2 = pExpr->op;
9967199747
pExpr->op = TK_TRUTH;
@@ -100164,29 +100240,28 @@
100164100240
/* Recursively resolve names in all subqueries
100165100241
*/
100166100242
for(i=0; i<p->pSrc->nSrc; i++){
100167100243
SrcItem *pItem = &p->pSrc->a[i];
100168100244
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
100169
- NameContext *pNC; /* Used to iterate name contexts */
100170
- int nRef = 0; /* Refcount for pOuterNC and outer contexts */
100245
+ int nRef = pOuterNC ? pOuterNC->nRef : 0;
100171100246
const char *zSavedContext = pParse->zAuthContext;
100172100247
100173
- /* Count the total number of references to pOuterNC and all of its
100174
- ** parent contexts. After resolving references to expressions in
100175
- ** pItem->pSelect, check if this value has changed. If so, then
100176
- ** SELECT statement pItem->pSelect must be correlated. Set the
100177
- ** pItem->fg.isCorrelated flag if this is the case. */
100178
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
100179
-
100180100248
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
100181100249
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
100182100250
pParse->zAuthContext = zSavedContext;
100183100251
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
100184100252
100185
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
100186
- assert( pItem->fg.isCorrelated==0 && nRef<=0 );
100187
- pItem->fg.isCorrelated = (nRef!=0);
100253
+ /* If the number of references to the outer context changed when
100254
+ ** expressions in the sub-select were resolved, the sub-select
100255
+ ** is correlated. It is not required to check the refcount on any
100256
+ ** but the innermost outer context object, as lookupName() increments
100257
+ ** the refcount on all contexts between the current one and the
100258
+ ** context containing the column when it resolves a name. */
100259
+ if( pOuterNC ){
100260
+ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
100261
+ pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
100262
+ }
100188100263
}
100189100264
}
100190100265
100191100266
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
100192100267
** resolve the result-set expression list.
@@ -103131,11 +103206,11 @@
103131103206
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
103132103207
pTab = p->pSrc->a[0].pTab;
103133103208
103134103209
/* Code an OP_Transaction and OP_TableLock for <table>. */
103135103210
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
103136
- assert( iDb>=0 && iDb<SQLITE_MAX_ATTACHED );
103211
+ assert( iDb>=0 && iDb<SQLITE_MAX_DB );
103137103212
sqlite3CodeVerifySchema(pParse, iDb);
103138103213
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
103139103214
103140103215
assert(v); /* sqlite3GetVdbe() has always been previously called */
103141103216
if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
@@ -107891,13 +107966,12 @@
107891107966
}
107892107967
if( rc==SQLITE_OK ){
107893107968
rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
107894107969
}
107895107970
assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
107896
- if( pStep->pUpsert ){
107971
+ if( pStep->pUpsert && rc==SQLITE_OK ){
107897107972
Upsert *pUpsert = pStep->pUpsert;
107898
- assert( rc==SQLITE_OK );
107899107973
pUpsert->pUpsertSrc = pSrc;
107900107974
sNC.uNC.pUpsert = pUpsert;
107901107975
sNC.ncFlags = NC_UUpsert;
107902107976
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
107903107977
if( rc==SQLITE_OK ){
@@ -108453,14 +108527,15 @@
108453108527
#ifndef SQLITE_OMIT_AUTHORIZATION
108454108528
sqlite3_xauth xAuth = db->xAuth;
108455108529
db->xAuth = 0;
108456108530
#endif
108457108531
108532
+ UNUSED_PARAMETER(NotUsed);
108458108533
rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
108459108534
if( rc!=SQLITE_OK ) goto drop_column_done;
108460108535
pTab = sParse.pNewTable;
108461
- if( pTab->nCol==1 || iCol>=pTab->nCol ){
108536
+ if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
108462108537
/* This can happen if the sqlite_schema table is corrupt */
108463108538
rc = SQLITE_CORRUPT_BKPT;
108464108539
goto drop_column_done;
108465108540
}
108466108541
@@ -112764,10 +112839,11 @@
112764112839
pParse->u1.pReturning = pRet;
112765112840
pRet->pParse = pParse;
112766112841
pRet->pReturnEL = pList;
112767112842
sqlite3ParserAddCleanup(pParse,
112768112843
(void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
112844
+ testcase( pParse->earlyCleanup );
112769112845
if( db->mallocFailed ) return;
112770112846
pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
112771112847
pRet->retTrig.op = TK_RETURNING;
112772112848
pRet->retTrig.tr_tm = TRIGGER_AFTER;
112773112849
pRet->retTrig.bReturning = 1;
@@ -114298,10 +114374,11 @@
114298114374
** the column names from the SELECT statement that defines the view.
114299114375
*/
114300114376
assert( pTable->aCol==0 );
114301114377
pTable->nCol = pSelTab->nCol;
114302114378
pTable->aCol = pSelTab->aCol;
114379
+ pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT);
114303114380
pSelTab->nCol = 0;
114304114381
pSelTab->aCol = 0;
114305114382
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
114306114383
}
114307114384
pTable->nNVCol = pTable->nCol;
@@ -116301,11 +116378,11 @@
116301116378
** later, by sqlite3FinishCoding().
116302116379
*/
116303116380
static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){
116304116381
assert( iDb>=0 && iDb<pToplevel->db->nDb );
116305116382
assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 );
116306
- assert( iDb<SQLITE_MAX_ATTACHED+2 );
116383
+ assert( iDb<SQLITE_MAX_DB );
116307116384
assert( sqlite3SchemaMutexHeld(pToplevel->db, iDb, 0) );
116308116385
if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
116309116386
DbMaskSet(pToplevel->cookieMask, iDb);
116310116387
if( !OMIT_TEMPDB && iDb==1 ){
116311116388
sqlite3OpenTempDatabase(pToplevel);
@@ -117505,14 +117582,16 @@
117505117582
}
117506117583
117507117584
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
117508117585
** and the SELECT subtree. */
117509117586
pSrc->a[0].pTab = 0;
117510
- pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
117587
+ pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);
117511117588
pSrc->a[0].pTab = pTab;
117512117589
if( pSrc->a[0].fg.isIndexedBy ){
117513117590
pSrc->a[0].u2.pIBIndex = 0;
117591
+ pSrc->a[0].fg.isIndexedBy = 0;
117592
+ sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy);
117514117593
}else if( pSrc->a[0].fg.isCte ){
117515117594
pSrc->a[0].u2.pCteUse->nUse++;
117516117595
}
117517117596
117518117597
/* generate the SELECT expression tree. */
@@ -129355,11 +129434,11 @@
129355129434
** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate
129356129435
**
129357129436
** Checkpoint the database.
129358129437
*/
129359129438
case PragTyp_WAL_CHECKPOINT: {
129360
- int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
129439
+ int iBt = (pId2->z?iDb:SQLITE_MAX_DB);
129361129440
int eMode = SQLITE_CHECKPOINT_PASSIVE;
129362129441
if( zRight ){
129363129442
if( sqlite3StrICmp(zRight, "full")==0 ){
129364129443
eMode = SQLITE_CHECKPOINT_FULL;
129365129444
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
@@ -130571,17 +130650,25 @@
130571130650
** for common cleanups that happen on most calls. But for less
130572130651
** common cleanups, we save a single NULL-pointer comparison in
130573130652
** sqlite3ParserReset(), which reduces the total CPU cycle count.
130574130653
**
130575130654
** If a memory allocation error occurs, then the cleanup happens immediately.
130576
-** When eithr SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
130655
+** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
130577130656
** pParse->earlyCleanup flag is set in that case. Calling code show verify
130578130657
** that test cases exist for which this happens, to guard against possible
130579130658
** use-after-free errors following an OOM. The preferred way to do this is
130580130659
** to immediately follow the call to this routine with:
130581130660
**
130582130661
** testcase( pParse->earlyCleanup );
130662
+**
130663
+** This routine returns a copy of its pPtr input (the third parameter)
130664
+** except if an early cleanup occurs, in which case it returns NULL. So
130665
+** another way to check for early cleanup is to check the return value.
130666
+** Or, stop using the pPtr parameter with this call and use only its
130667
+** return value thereafter. Something like this:
130668
+**
130669
+** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj);
130583130670
*/
130584130671
SQLITE_PRIVATE void *sqlite3ParserAddCleanup(
130585130672
Parse *pParse, /* Destroy when this Parser finishes */
130586130673
void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
130587130674
void *pPtr /* Pointer to object to be cleaned up */
@@ -131075,16 +131162,20 @@
131075131162
sqlite3ExprDelete(db, p->pWhere);
131076131163
sqlite3ExprListDelete(db, p->pGroupBy);
131077131164
sqlite3ExprDelete(db, p->pHaving);
131078131165
sqlite3ExprListDelete(db, p->pOrderBy);
131079131166
sqlite3ExprDelete(db, p->pLimit);
131167
+ if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
131080131168
#ifndef SQLITE_OMIT_WINDOWFUNC
131081131169
if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
131082131170
sqlite3WindowListDelete(db, p->pWinDefn);
131083131171
}
131172
+ while( p->pWin ){
131173
+ assert( p->pWin->ppThis==&p->pWin );
131174
+ sqlite3WindowUnlinkFromSelect(p->pWin);
131175
+ }
131084131176
#endif
131085
- if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
131086131177
if( bFree ) sqlite3DbFreeNN(db, p);
131087131178
p = pPrior;
131088131179
bFree = 1;
131089131180
}
131090131181
}
@@ -131396,10 +131487,13 @@
131396131487
static void unsetJoinExpr(Expr *p, int iTable){
131397131488
while( p ){
131398131489
if( ExprHasProperty(p, EP_FromJoin)
131399131490
&& (iTable<0 || p->iRightJoinTable==iTable) ){
131400131491
ExprClearProperty(p, EP_FromJoin);
131492
+ }
131493
+ if( p->op==TK_COLUMN && p->iTable==iTable ){
131494
+ ExprClearProperty(p, EP_CanBeNull);
131401131495
}
131402131496
if( p->op==TK_FUNCTION && p->x.pList ){
131403131497
int i;
131404131498
for(i=0; i<p->x.pList->nExpr; i++){
131405131499
unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
@@ -132373,11 +132467,11 @@
132373132467
}
132374132468
132375132469
/*
132376132470
** Name of the connection operator, used for error messages.
132377132471
*/
132378
-static const char *selectOpName(int id){
132472
+SQLITE_PRIVATE const char *sqlite3SelectOpName(int id){
132379132473
char *z;
132380132474
switch( id ){
132381132475
case TK_ALL: z = "UNION ALL"; break;
132382132476
case TK_INTERSECT: z = "INTERSECT"; break;
132383132477
case TK_EXCEPT: z = "EXCEPT"; break;
@@ -133592,16 +133686,12 @@
133592133686
assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
133593133687
assert( p->selFlags & SF_Compound );
133594133688
db = pParse->db;
133595133689
pPrior = p->pPrior;
133596133690
dest = *pDest;
133597
- if( pPrior->pOrderBy || pPrior->pLimit ){
133598
- sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
133599
- pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op));
133600
- rc = 1;
133601
- goto multi_select_end;
133602
- }
133691
+ assert( pPrior->pOrderBy==0 );
133692
+ assert( pPrior->pLimit==0 );
133603133693
133604133694
v = sqlite3GetVdbe(pParse);
133605133695
assert( v!=0 ); /* The VDBE already created by calling function */
133606133696
133607133697
/* Create the destination temporary table if necessary
@@ -133654,11 +133744,11 @@
133654133744
assert( !pPrior->pLimit );
133655133745
pPrior->iLimit = p->iLimit;
133656133746
pPrior->iOffset = p->iOffset;
133657133747
pPrior->pLimit = p->pLimit;
133658133748
rc = sqlite3Select(pParse, pPrior, &dest);
133659
- p->pLimit = 0;
133749
+ pPrior->pLimit = 0;
133660133750
if( rc ){
133661133751
goto multi_select_end;
133662133752
}
133663133753
p->pPrior = 0;
133664133754
p->iLimit = pPrior->iLimit;
@@ -133675,12 +133765,12 @@
133675133765
rc = sqlite3Select(pParse, p, &dest);
133676133766
testcase( rc!=SQLITE_OK );
133677133767
pDelete = p->pPrior;
133678133768
p->pPrior = pPrior;
133679133769
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
133680
- if( pPrior->pLimit
133681
- && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
133770
+ if( p->pLimit
133771
+ && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit)
133682133772
&& nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
133683133773
){
133684133774
p->nSelectRow = sqlite3LogEst((u64)nLimit);
133685133775
}
133686133776
if( addr ){
@@ -133740,11 +133830,11 @@
133740133830
p->pPrior = 0;
133741133831
pLimit = p->pLimit;
133742133832
p->pLimit = 0;
133743133833
uniondest.eDest = op;
133744133834
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
133745
- selectOpName(p->op)));
133835
+ sqlite3SelectOpName(p->op)));
133746133836
rc = sqlite3Select(pParse, p, &uniondest);
133747133837
testcase( rc!=SQLITE_OK );
133748133838
assert( p->pOrderBy==0 );
133749133839
pDelete = p->pPrior;
133750133840
p->pPrior = pPrior;
@@ -133816,11 +133906,11 @@
133816133906
p->pPrior = 0;
133817133907
pLimit = p->pLimit;
133818133908
p->pLimit = 0;
133819133909
intersectdest.iSDParm = tab2;
133820133910
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
133821
- selectOpName(p->op)));
133911
+ sqlite3SelectOpName(p->op)));
133822133912
rc = sqlite3Select(pParse, p, &intersectdest);
133823133913
testcase( rc!=SQLITE_OK );
133824133914
pDelete = p->pPrior;
133825133915
p->pPrior = pPrior;
133826133916
if( p->nSelectRow>pPrior->nSelectRow ){
@@ -133925,11 +134015,12 @@
133925134015
SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
133926134016
if( p->selFlags & SF_Values ){
133927134017
sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
133928134018
}else{
133929134019
sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
133930
- " do not have the same number of result columns", selectOpName(p->op));
134020
+ " do not have the same number of result columns",
134021
+ sqlite3SelectOpName(p->op));
133931134022
}
133932134023
}
133933134024
133934134025
/*
133935134026
** Code an output subroutine for a coroutine implementation of a
@@ -134022,14 +134113,12 @@
134022134113
** store the results in the appropriate memory cell and break out
134023134114
** of the scan loop. Note that the select might return multiple columns
134024134115
** if it is the RHS of a row-value IN operator.
134025134116
*/
134026134117
case SRT_Mem: {
134027
- if( pParse->nErr==0 ){
134028
- testcase( pIn->nSdst>1 );
134029
- sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
134030
- }
134118
+ testcase( pIn->nSdst>1 );
134119
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
134031134120
/* The LIMIT clause will jump out of the loop for us */
134032134121
break;
134033134122
}
134034134123
#endif /* #ifndef SQLITE_OMIT_SUBQUERY */
134035134124
@@ -134317,11 +134406,11 @@
134317134406
regOutA = ++pParse->nMem;
134318134407
regOutB = ++pParse->nMem;
134319134408
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
134320134409
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
134321134410
134322
- ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op)));
134411
+ ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlite3SelectOpName(p->op)));
134323134412
134324134413
/* Generate a coroutine to evaluate the SELECT statement to the
134325134414
** left of the compound operator - the "A" select.
134326134415
*/
134327134416
addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
@@ -135479,10 +135568,39 @@
135479135568
}
135480135569
}while( x.nChng );
135481135570
return nChng;
135482135571
}
135483135572
135573
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
135574
+# if !defined(SQLITE_OMIT_WINDOWFUNC)
135575
+/*
135576
+** This function is called to determine whether or not it is safe to
135577
+** push WHERE clause expression pExpr down to FROM clause sub-query
135578
+** pSubq, which contains at least one window function. Return 1
135579
+** if it is safe and the expression should be pushed down, or 0
135580
+** otherwise.
135581
+**
135582
+** It is only safe to push the expression down if it consists only
135583
+** of constants and copies of expressions that appear in the PARTITION
135584
+** BY clause of all window function used by the sub-query. It is safe
135585
+** to filter out entire partitions, but not rows within partitions, as
135586
+** this may change the results of the window functions.
135587
+**
135588
+** At the time this function is called it is guaranteed that
135589
+**
135590
+** * the sub-query uses only one distinct window frame, and
135591
+** * that the window frame has a PARTITION BY clase.
135592
+*/
135593
+static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
135594
+ assert( pSubq->pWin->pPartition );
135595
+ assert( (pSubq->selFlags & SF_MultiPart)==0 );
135596
+ assert( pSubq->pPrior==0 );
135597
+ return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition);
135598
+}
135599
+# endif /* SQLITE_OMIT_WINDOWFUNC */
135600
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
135601
+
135484135602
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
135485135603
/*
135486135604
** Make copies of relevant WHERE clause terms of the outer query into
135487135605
** the WHERE clause of subquery. Example:
135488135606
**
@@ -135526,13 +135644,24 @@
135526135644
**
135527135645
** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9).
135528135646
** But if the (b2=2) term were to be pushed down into the bb subquery,
135529135647
** then the (1,1,NULL) row would be suppressed.
135530135648
**
135531
-** (6) The inner query features one or more window-functions (since
135532
-** changes to the WHERE clause of the inner query could change the
135533
-** window over which window functions are calculated).
135649
+** (6) Window functions make things tricky as changes to the WHERE clause
135650
+** of the inner query could change the window over which window
135651
+** functions are calculated. Therefore, do not attempt the optimization
135652
+** if:
135653
+**
135654
+** (6a) The inner query uses multiple incompatible window partitions.
135655
+**
135656
+** (6b) The inner query is a compound and uses window-functions.
135657
+**
135658
+** (6c) The WHERE clause does not consist entirely of constants and
135659
+** copies of expressions found in the PARTITION BY clause of
135660
+** all window-functions used by the sub-query. It is safe to
135661
+** filter out entire partitions, as this does not change the
135662
+** window over which any window-function is calculated.
135534135663
**
135535135664
** (7) The inner query is a Common Table Expression (CTE) that should
135536135665
** be materialized. (This restriction is implemented in the calling
135537135666
** routine.)
135538135667
**
@@ -135546,17 +135675,21 @@
135546135675
int iCursor, /* Cursor number of the subquery */
135547135676
int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
135548135677
){
135549135678
Expr *pNew;
135550135679
int nChng = 0;
135551
- Select *pSel;
135552135680
if( pWhere==0 ) return 0;
135553
- if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */
135681
+ if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
135554135682
135555135683
#ifndef SQLITE_OMIT_WINDOWFUNC
135556
- for(pSel=pSubq; pSel; pSel=pSel->pPrior){
135557
- if( pSel->pWin ) return 0; /* restriction (6) */
135684
+ if( pSubq->pPrior ){
135685
+ Select *pSel;
135686
+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){
135687
+ if( pSel->pWin ) return 0; /* restriction (6b) */
135688
+ }
135689
+ }else{
135690
+ if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
135558135691
}
135559135692
#endif
135560135693
135561135694
#ifdef SQLITE_DEBUG
135562135695
/* Only the first term of a compound can have a WITH clause. But make
@@ -135599,10 +135732,18 @@
135599135732
x.iTable = iCursor;
135600135733
x.iNewTable = iCursor;
135601135734
x.isLeftJoin = 0;
135602135735
x.pEList = pSubq->pEList;
135603135736
pNew = substExpr(&x, pNew);
135737
+#ifndef SQLITE_OMIT_WINDOWFUNC
135738
+ if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
135739
+ /* Restriction 6c has prevented push-down in this case */
135740
+ sqlite3ExprDelete(pParse->db, pNew);
135741
+ nChng--;
135742
+ break;
135743
+ }
135744
+#endif
135604135745
if( pSubq->selFlags & SF_Aggregate ){
135605135746
pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
135606135747
}else{
135607135748
pSubq->pWhere = sqlite3ExprAnd(pParse, pSubq->pWhere, pNew);
135608135749
}
@@ -136190,11 +136331,14 @@
136190136331
if( IsVirtual(pTab) || pTab->pSelect ){
136191136332
i16 nCol;
136192136333
u8 eCodeOrig = pWalker->eCode;
136193136334
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
136194136335
assert( pFrom->pSelect==0 );
136195
- if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){
136336
+ if( pTab->pSelect
136337
+ && (db->flags & SQLITE_EnableView)==0
136338
+ && pTab->pSchema!=db->aDb[1].pSchema
136339
+ ){
136196136340
sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
136197136341
pTab->zName);
136198136342
}
136199136343
#ifndef SQLITE_OMIT_VIRTUALTABLE
136200136344
if( IsVirtual(pTab)
@@ -136969,12 +137113,23 @@
136969137113
if( IgnorableDistinct(pDest) ){
136970137114
assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
136971137115
pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
136972137116
pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo );
136973137117
/* All of these destinations are also able to ignore the ORDER BY clause */
136974
- sqlite3ExprListDelete(db, p->pOrderBy);
136975
- p->pOrderBy = 0;
137118
+ if( p->pOrderBy ){
137119
+#if SELECTTRACE_ENABLED
137120
+ SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
137121
+ if( sqlite3SelectTrace & 0x100 ){
137122
+ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
137123
+ }
137124
+#endif
137125
+ sqlite3ParserAddCleanup(pParse,
137126
+ (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
137127
+ p->pOrderBy);
137128
+ testcase( pParse->earlyCleanup );
137129
+ p->pOrderBy = 0;
137130
+ }
136976137131
p->selFlags &= ~SF_Distinct;
136977137132
p->selFlags |= SF_NoopOrderBy;
136978137133
}
136979137134
sqlite3SelectPrep(pParse, p, 0);
136980137135
if( pParse->nErr || db->mallocFailed ){
@@ -137589,10 +137744,11 @@
137589137744
*/
137590137745
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
137591137746
if( pAggInfo ){
137592137747
sqlite3ParserAddCleanup(pParse,
137593137748
(void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
137749
+ testcase( pParse->earlyCleanup );
137594137750
}
137595137751
if( db->mallocFailed ){
137596137752
goto select_end;
137597137753
}
137598137754
pAggInfo->selId = p->selId;
@@ -147345,11 +147501,14 @@
147345147501
sqlite3 *db = pParse->db;
147346147502
147347147503
assert( pExpr->op==TK_EXISTS );
147348147504
assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
147349147505
147350
- if( (pSel->selFlags & SF_Aggregate) || pSel->pWin ) return;
147506
+ if( pSel->selFlags & SF_Aggregate ) return;
147507
+#ifndef SQLITE_OMIT_WINDOWFUNC
147508
+ if( pSel->pWin ) return;
147509
+#endif
147351147510
if( pSel->pPrior ) return;
147352147511
if( pSel->pWhere==0 ) return;
147353147512
if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;
147354147513
147355147514
pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -147542,10 +147701,16 @@
147542147701
pNew->u.x.leftColumn = aiCurCol[1];
147543147702
testcase( (prereqLeft | extraRight) != prereqLeft );
147544147703
pNew->prereqRight = prereqLeft | extraRight;
147545147704
pNew->prereqAll = prereqAll;
147546147705
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
147706
+ }else if( op==TK_ISNULL && 0==sqlite3ExprCanBeNull(pLeft) ){
147707
+ pExpr->op = TK_TRUEFALSE;
147708
+ pExpr->u.zToken = "false";
147709
+ ExprSetProperty(pExpr, EP_IsFalse);
147710
+ pTerm->prereqAll = 0;
147711
+ pTerm->eOperator = 0;
147547147712
}
147548147713
}
147549147714
147550147715
#ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
147551147716
/* If a term is the BETWEEN operator, create two new virtual terms
@@ -153019,11 +153184,11 @@
153019153184
}
153020153185
}
153021153186
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
153022153187
pWInfo->revMask = ALLBITS;
153023153188
}
153024
- if( pParse->nErr || NEVER(db->mallocFailed) ){
153189
+ if( pParse->nErr || db->mallocFailed ){
153025153190
goto whereBeginError;
153026153191
}
153027153192
#ifdef WHERETRACE_ENABLED
153028153193
if( sqlite3WhereTrace ){
153029153194
sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
@@ -154961,19 +155126,23 @@
154961155126
** to be processed as part of SELECT statement pSel). The window is linked
154962155127
** in if either (a) there are no other windows already linked to this
154963155128
** SELECT, or (b) the windows already linked use a compatible window frame.
154964155129
*/
154965155130
SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){
154966
- if( pSel!=0
154967
- && (0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0))
154968
- ){
154969
- pWin->pNextWin = pSel->pWin;
154970
- if( pSel->pWin ){
154971
- pSel->pWin->ppThis = &pWin->pNextWin;
154972
- }
154973
- pSel->pWin = pWin;
154974
- pWin->ppThis = &pSel->pWin;
155131
+ if( pSel ){
155132
+ if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){
155133
+ pWin->pNextWin = pSel->pWin;
155134
+ if( pSel->pWin ){
155135
+ pSel->pWin->ppThis = &pWin->pNextWin;
155136
+ }
155137
+ pSel->pWin = pWin;
155138
+ pWin->ppThis = &pSel->pWin;
155139
+ }else{
155140
+ if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){
155141
+ pSel->selFlags |= SF_MultiPart;
155142
+ }
155143
+ }
154975155144
}
154976155145
}
154977155146
154978155147
/*
154979155148
** Return 0 if the two window objects are identical, 1 if they are
@@ -155718,10 +155887,11 @@
155718155887
int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */
155719155888
int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */
155720155889
int regString = ++pParse->nMem; /* Reg. for constant value '' */
155721155890
int arith = OP_Add; /* OP_Add or OP_Subtract */
155722155891
int addrGe; /* Jump destination */
155892
+ CollSeq *pColl;
155723155893
155724155894
assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
155725155895
assert( pOrderBy && pOrderBy->nExpr==1 );
155726155896
if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
155727155897
switch( op ){
@@ -155808,10 +155978,12 @@
155808155978
155809155979
/* Compare registers reg2 and reg1, taking the jump if required. Note that
155810155980
** control skips over this test if the BIGNULL flag is set and either
155811155981
** reg1 or reg2 contain a NULL value. */
155812155982
sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
155983
+ pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr);
155984
+ sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ);
155813155985
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
155814155986
155815155987
assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
155816155988
testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
155817155989
testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
@@ -156814,15 +156986,25 @@
156814156986
** SQLITE_LIMIT_COMPOUND_SELECT.
156815156987
*/
156816156988
static void parserDoubleLinkSelect(Parse *pParse, Select *p){
156817156989
assert( p!=0 );
156818156990
if( p->pPrior ){
156819
- Select *pNext = 0, *pLoop;
156820
- int mxSelect, cnt = 0;
156821
- for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){
156991
+ Select *pNext = 0, *pLoop = p;
156992
+ int mxSelect, cnt = 1;
156993
+ while(1){
156822156994
pLoop->pNext = pNext;
156823156995
pLoop->selFlags |= SF_Compound;
156996
+ pNext = pLoop;
156997
+ pLoop = pLoop->pPrior;
156998
+ if( pLoop==0 ) break;
156999
+ cnt++;
157000
+ if( pLoop->pOrderBy || pLoop->pLimit ){
157001
+ sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
157002
+ pLoop->pOrderBy!=0 ? "ORDER BY" : "LIMIT",
157003
+ sqlite3SelectOpName(pNext->op));
157004
+ break;
157005
+ }
156824157006
}
156825157007
if( (p->selFlags & SF_MultiValue)==0 &&
156826157008
(mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 &&
156827157009
cnt>mxSelect
156828157010
){
@@ -166129,11 +166311,11 @@
166129166311
){
166130166312
#ifdef SQLITE_OMIT_WAL
166131166313
return SQLITE_OK;
166132166314
#else
166133166315
int rc; /* Return code */
166134
- int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
166316
+ int iDb; /* Schema to checkpoint */
166135166317
166136166318
#ifdef SQLITE_ENABLE_API_ARMOR
166137166319
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
166138166320
#endif
166139166321
@@ -166152,10 +166334,12 @@
166152166334
}
166153166335
166154166336
sqlite3_mutex_enter(db->mutex);
166155166337
if( zDb && zDb[0] ){
166156166338
iDb = sqlite3FindDbName(db, zDb);
166339
+ }else{
166340
+ iDb = SQLITE_MAX_DB; /* This means process all schemas */
166157166341
}
166158166342
if( iDb<0 ){
166159166343
rc = SQLITE_ERROR;
166160166344
sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
166161166345
}else{
@@ -166200,11 +166384,11 @@
166200166384
**
166201166385
** The mutex on database handle db should be held by the caller. The mutex
166202166386
** associated with the specific b-tree being checkpointed is taken by
166203166387
** this function while the checkpoint is running.
166204166388
**
166205
-** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are
166389
+** If iDb is passed SQLITE_MAX_DB then all attached databases are
166206166390
** checkpointed. If an error is encountered it is returned immediately -
166207166391
** no attempt is made to checkpoint any remaining databases.
166208166392
**
166209166393
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART
166210166394
** or TRUNCATE.
@@ -166215,13 +166399,15 @@
166215166399
int bBusy = 0; /* True if SQLITE_BUSY has been encountered */
166216166400
166217166401
assert( sqlite3_mutex_held(db->mutex) );
166218166402
assert( !pnLog || *pnLog==-1 );
166219166403
assert( !pnCkpt || *pnCkpt==-1 );
166404
+ testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */
166405
+ testcase( iDb==SQLITE_MAX_DB );
166220166406
166221166407
for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
166222
- if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
166408
+ if( i==iDb || iDb==SQLITE_MAX_DB ){
166223166409
rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
166224166410
pnLog = 0;
166225166411
pnCkpt = 0;
166226166412
if( rc==SQLITE_BUSY ){
166227166413
bBusy = 1;
@@ -174914,13 +175100,13 @@
174914175100
res = fts3PoslistNearMerge(
174915175101
&pOut, aTmp, nParam1, nParam2, paPoslist, &p2
174916175102
);
174917175103
if( res ){
174918175104
nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
174919
- if( nNew>=0 ){
175105
+ assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 );
175106
+ if( nNew>=0 && nNew<=pPhrase->doclist.nList ){
174920175107
assert( pPhrase->doclist.pList[nNew]=='\0' );
174921
- assert( nNew<=pPhrase->doclist.nList && nNew>0 );
174922175108
memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
174923175109
pPhrase->doclist.nList = nNew;
174924175110
}
174925175111
*paPoslist = pPhrase->doclist.pList;
174926175112
*pnToken = pPhrase->nToken;
@@ -176850,10 +177036,15 @@
176850177036
176851177037
if( sqlite3_fts3_enable_parentheses ){
176852177038
if( *zInput=='(' ){
176853177039
int nConsumed = 0;
176854177040
pParse->nNest++;
177041
+#if !defined(SQLITE_MAX_EXPR_DEPTH)
177042
+ if( pParse->nNest>1000 ) return SQLITE_ERROR;
177043
+#elif SQLITE_MAX_EXPR_DEPTH>0
177044
+ if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR;
177045
+#endif
176855177046
rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
176856177047
*pnConsumed = (int)(zInput - z) + 1 + nConsumed;
176857177048
return rc;
176858177049
}else if( *zInput==')' ){
176859177050
pParse->nNest--;
@@ -184253,31 +184444,30 @@
184253184444
if( pNode->block.a){
184254184445
rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
184255184446
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
184256184447
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
184257184448
if( rc==SQLITE_OK ){
184258
- if( reader.term.n<=0 ){
184259
- rc = FTS_CORRUPT_VTAB;
184260
- }else{
184449
+ assert_fts3_nc( reader.term.n>0 || reader.aNode==0 );
184450
+ if( reader.term.n>0 ){
184261184451
memcpy(pNode->key.a, reader.term.a, reader.term.n);
184262
- pNode->key.n = reader.term.n;
184263
- if( i>0 ){
184264
- char *aBlock = 0;
184265
- int nBlock = 0;
184266
- pNode = &pWriter->aNodeWriter[i-1];
184267
- pNode->iBlock = reader.iChild;
184268
- rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
184269
- blobGrowBuffer(&pNode->block,
184270
- MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
184271
- );
184272
- if( rc==SQLITE_OK ){
184273
- memcpy(pNode->block.a, aBlock, nBlock);
184274
- pNode->block.n = nBlock;
184275
- memset(&pNode->block.a[nBlock], 0, FTS3_NODE_PADDING);
184276
- }
184277
- sqlite3_free(aBlock);
184278
- }
184452
+ }
184453
+ pNode->key.n = reader.term.n;
184454
+ if( i>0 ){
184455
+ char *aBlock = 0;
184456
+ int nBlock = 0;
184457
+ pNode = &pWriter->aNodeWriter[i-1];
184458
+ pNode->iBlock = reader.iChild;
184459
+ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
184460
+ blobGrowBuffer(&pNode->block,
184461
+ MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
184462
+ );
184463
+ if( rc==SQLITE_OK ){
184464
+ memcpy(pNode->block.a, aBlock, nBlock);
184465
+ pNode->block.n = nBlock;
184466
+ memset(&pNode->block.a[nBlock], 0, FTS3_NODE_PADDING);
184467
+ }
184468
+ sqlite3_free(aBlock);
184279184469
}
184280184470
}
184281184471
}
184282184472
nodeReaderRelease(&reader);
184283184473
}
@@ -187755,10 +187945,11 @@
187755187945
memset(pCsr, 0, sizeof(unicode_cursor));
187756187946
187757187947
pCsr->aInput = (const unsigned char *)aInput;
187758187948
if( aInput==0 ){
187759187949
pCsr->nInput = 0;
187950
+ pCsr->aInput = (const unsigned char*)"";
187760187951
}else if( nInput<0 ){
187761187952
pCsr->nInput = (int)strlen(aInput);
187762187953
}else{
187763187954
pCsr->nInput = nInput;
187764187955
}
@@ -224323,21 +224514,26 @@
224323224514
pIter->nPoslist = ((int)(p[0])) >> 1;
224324224515
pIter->nSize = 1;
224325224516
}
224326224517
224327224518
pIter->aPoslist = p;
224519
+ if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){
224520
+ pIter->aPoslist = 0;
224521
+ }
224328224522
}
224329224523
}
224330224524
224331224525
static void fts5DoclistIterInit(
224332224526
Fts5Buffer *pBuf,
224333224527
Fts5DoclistIter *pIter
224334224528
){
224335224529
memset(pIter, 0, sizeof(*pIter));
224336
- pIter->aPoslist = pBuf->p;
224337
- pIter->aEof = &pBuf->p[pBuf->n];
224338
- fts5DoclistIterNext(pIter);
224530
+ if( pBuf->n>0 ){
224531
+ pIter->aPoslist = pBuf->p;
224532
+ pIter->aEof = &pBuf->p[pBuf->n];
224533
+ fts5DoclistIterNext(pIter);
224534
+ }
224339224535
}
224340224536
224341224537
#if 0
224342224538
/*
224343224539
** Append a doclist to buffer pBuf.
@@ -225095,12 +225291,13 @@
225095225291
** Return the current term.
225096225292
*/
225097225293
static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
225098225294
int n;
225099225295
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
225296
+ assert_nc( z || n<=1 );
225100225297
*pn = n-1;
225101
- return &z[1];
225298
+ return (z ? &z[1] : 0);
225102225299
}
225103225300
225104225301
/*
225105225302
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
225106225303
*/
@@ -228382,11 +228579,12 @@
228382228579
){
228383228580
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
228384228581
int n;
228385228582
int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
228386228583
if( rc==SQLITE_OK ){
228387
- pIter->b = &pIter->a[n];
228584
+ assert( pIter->a || n==0 );
228585
+ pIter->b = (pIter->a ? &pIter->a[n] : 0);
228388228586
*piCol = 0;
228389228587
*piOff = 0;
228390228588
fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
228391228589
}
228392228590
return rc;
@@ -228441,19 +228639,21 @@
228441228639
pIter->a = &pSorter->aPoslist[i1];
228442228640
}else{
228443228641
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
228444228642
}
228445228643
if( rc==SQLITE_OK ){
228446
- pIter->b = &pIter->a[n];
228644
+ assert( pIter->a || n==0 );
228645
+ pIter->b = (pIter->a ? &pIter->a[n] : 0);
228447228646
*piCol = 0;
228448228647
fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
228449228648
}
228450228649
}else{
228451228650
int n;
228452228651
rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
228453228652
if( rc==SQLITE_OK ){
228454
- pIter->b = &pIter->a[n];
228653
+ assert( pIter->a || n==0 );
228654
+ pIter->b = (pIter->a ? &pIter->a[n] : 0);
228455228655
if( n<=0 ){
228456228656
*piCol = -1;
228457228657
}else if( pIter->a[0]==0x01 ){
228458228658
pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
228459228659
}else{
@@ -228927,11 +229127,11 @@
228927229127
assert( nArg>0 );
228928229128
rc = SQLITE_ERROR;
228929229129
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
228930229130
}else{
228931229131
rc = pMod->x.xCreate(
228932
- pMod->pUserData, &azArg[1], (nArg?nArg-1:0), &pConfig->pTok
229132
+ pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
228933229133
);
228934229134
pConfig->pTokApi = &pMod->x;
228935229135
if( rc!=SQLITE_OK ){
228936229136
if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
228937229137
}else{
@@ -228990,11 +229190,11 @@
228990229190
int nArg, /* Number of args */
228991229191
sqlite3_value **apUnused /* Function arguments */
228992229192
){
228993229193
assert( nArg==0 );
228994229194
UNUSED_PARAM2(nArg, apUnused);
228995
- sqlite3_result_text(pCtx, "fts5: 2021-02-22 11:07:25 b66a49570852cf118a372a6ac44be3070cf9b4254696f16315b7c79a614e6c35", -1, SQLITE_TRANSIENT);
229195
+ sqlite3_result_text(pCtx, "fts5: 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b", -1, SQLITE_TRANSIENT);
228996229196
}
228997229197
228998229198
/*
228999229199
** Return true if zName is the extension on one of the shadow tables used
229000229200
** by this module.
@@ -233916,12 +234116,12 @@
233916234116
}
233917234117
#endif /* SQLITE_CORE */
233918234118
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
233919234119
233920234120
/************** End of stmt.c ************************************************/
233921
-#if __LINE__!=233921
234121
+#if __LINE__!=234121
233922234122
#undef SQLITE_SOURCE_ID
233923
-#define SQLITE_SOURCE_ID "2021-02-22 16:42:09 b5a0778cc5a98a864bea72670f83262da940aceb91fa4cdf46ec097337a3alt2"
234123
+#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115alt2"
233924234124
#endif
233925234125
/* Return the source-id for this library */
233926234126
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
233927234127
/************************** End of sqlite3.c ******************************/
233928234128
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1186,11 +1186,11 @@
1186 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1187 ** [sqlite_version()] and [sqlite_source_id()].
1188 */
1189 #define SQLITE_VERSION "3.35.0"
1190 #define SQLITE_VERSION_NUMBER 3035000
1191 #define SQLITE_SOURCE_ID "2021-02-22 16:42:09 b5a0778cc5a98a864bea72670f83262da940aceb91fa4cdf46ec097337a3alt1"
1192
1193 /*
1194 ** CAPI3REF: Run-Time Library Version Numbers
1195 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1196 **
@@ -3193,11 +3193,17 @@
3193 ** The first argument is an integer which is 0 to disable views,
3194 ** positive to enable views or negative to leave the setting unchanged.
3195 ** The second parameter is a pointer to an integer into which
3196 ** is written 0 or 1 to indicate whether views are disabled or enabled
3197 ** following this call. The second parameter may be a NULL pointer, in
3198 ** which case the view setting is not reported back. </dd>
 
 
 
 
 
 
3199 **
3200 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
3201 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
3202 ** <dd> ^This option is used to enable or disable the
3203 ** [fts3_tokenizer()] function which is part of the
@@ -11617,22 +11623,27 @@
11617 ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
11618 ** created by [sqlite3changeset_start()]. In the latter case, the most recent
11619 ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
11620 ** is not the case, this function returns [SQLITE_MISUSE].
11621 **
11622 ** If argument pzTab is not NULL, then *pzTab is set to point to a
11623 ** nul-terminated utf-8 encoded string containing the name of the table
11624 ** affected by the current change. The buffer remains valid until either
11625 ** sqlite3changeset_next() is called on the iterator or until the
11626 ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
11627 ** set to the number of columns in the table affected by the change. If
11628 ** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
 
 
 
 
 
 
 
11629 ** is an indirect change, or false (0) otherwise. See the documentation for
11630 ** [sqlite3session_indirect()] for a description of direct and indirect
11631 ** changes. Finally, if pOp is not NULL, then *pOp is set to one of
11632 ** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
11633 ** type of change that the iterator currently points to.
11634 **
11635 ** If no error occurs, SQLITE_OK is returned. If an error does occur, an
11636 ** SQLite error code is returned. The values of the output variables may not
11637 ** be trusted in this case.
11638 */
@@ -15368,11 +15379,11 @@
15368
15369 /* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
15370 #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
15371 #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
15372 #define BTREE_APPEND 0x08 /* Insert is likely an append */
15373 #define BTREE_PREFORMAT 0x80 /* Insert is likely an append */
15374
15375 /* An instance of the BtreePayload object describes the content of a single
15376 ** entry in either an index or table btree.
15377 **
15378 ** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
@@ -16942,10 +16953,15 @@
16942 #define SQLITE_TRACE_LEGACY 0
16943 #define SQLITE_TRACE_XPROFILE 0
16944 #endif /* SQLITE_OMIT_DEPRECATED */
16945 #define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */
16946
 
 
 
 
 
16947
16948 /*
16949 ** Each database connection is an instance of the following structure.
16950 */
16951 struct sqlite3 {
@@ -18665,10 +18681,11 @@
18665 #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */
18666 #define SF_View 0x0200000 /* SELECT statement is a view */
18667 #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
18668 #define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
18669 #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
 
18670
18671 /*
18672 ** The results of a SELECT can be distributed in several ways, as defined
18673 ** by one of the following macros. The "SRT" prefix means "SELECT Result
18674 ** Type".
@@ -20255,10 +20272,11 @@
20255 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
20256 SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
20257 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
20258 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
20259 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
 
20260 SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*);
20261
20262 #ifdef SQLITE_DEBUG
20263 SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
20264 #endif
@@ -29329,11 +29347,11 @@
29329 setStrAccumError(p, SQLITE_TOOBIG);
29330 return p->nAlloc - p->nChar - 1;
29331 }else{
29332 char *zOld = isMalloced(p) ? p->zText : 0;
29333 i64 szNew = p->nChar;
29334 szNew += N + 1;
29335 if( szNew+p->nChar<=p->mxAlloc ){
29336 /* Force exponential buffer size growth as long as it does not overflow,
29337 ** to avoid having to call this routine too often */
29338 szNew += p->nChar;
29339 }
@@ -50636,10 +50654,11 @@
50636 #endif
50637 p->page.pBuf = pPg;
50638 p->page.pExtra = &p[1];
50639 p->isBulkLocal = 0;
50640 p->isAnchor = 0;
 
50641 }
50642 (*pCache->pnPurgeable)++;
50643 return p;
50644 }
50645
@@ -52554,10 +52573,11 @@
52554 i64 iOffset; /* Starting offset in main journal */
52555 i64 iHdrOffset; /* See above */
52556 Bitvec *pInSavepoint; /* Set of pages in this savepoint */
52557 Pgno nOrig; /* Original number of pages in file */
52558 Pgno iSubRec; /* Index of first record in sub-journal */
 
52559 #ifndef SQLITE_OMIT_WAL
52560 u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
52561 #endif
52562 };
52563
@@ -53189,10 +53209,13 @@
53189 Pgno pgno = pPg->pgno;
53190 int i;
53191 for(i=0; i<pPager->nSavepoint; i++){
53192 p = &pPager->aSavepoint[i];
53193 if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
 
 
 
53194 return 1;
53195 }
53196 }
53197 return 0;
53198 }
@@ -58967,10 +58990,11 @@
58967 }else{
58968 aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
58969 }
58970 aNew[ii].iSubRec = pPager->nSubRec;
58971 aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
 
58972 if( !aNew[ii].pInSavepoint ){
58973 return SQLITE_NOMEM_BKPT;
58974 }
58975 if( pagerUseWal(pPager) ){
58976 sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
@@ -59048,17 +59072,19 @@
59048 pPager->nSavepoint = nNew;
59049
59050 /* If this is a release of the outermost savepoint, truncate
59051 ** the sub-journal to zero bytes in size. */
59052 if( op==SAVEPOINT_RELEASE ){
59053 if( nNew==0 && isOpen(pPager->sjfd) ){
 
59054 /* Only truncate if it is an in-memory sub-journal. */
59055 if( sqlite3JournalIsInMemory(pPager->sjfd) ){
59056 rc = sqlite3OsTruncate(pPager->sjfd, 0);
 
59057 assert( rc==SQLITE_OK );
59058 }
59059 pPager->nSubRec = 0;
59060 }
59061 }
59062 /* Else this is a rollback operation, playback the specified savepoint.
59063 ** If this is a temp-file, it is possible that the journal file has
59064 ** not yet been opened. In this case there have been no changes to
@@ -72558,11 +72584,13 @@
72558 }else{
72559 pRight = findCell(pParent, i+nxDiv-pParent->nOverflow);
72560 }
72561 pgno = get4byte(pRight);
72562 while( 1 ){
72563 rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
 
 
72564 if( rc ){
72565 memset(apOld, 0, (i+1)*sizeof(MemPage*));
72566 goto balance_cleanup;
72567 }
72568 if( apOld[i]->nFree<0 ){
@@ -72597,16 +72625,14 @@
72597 ** buffer. It will be copied out again as soon as the aSpace[] buffer
72598 ** is allocated. */
72599 if( pBt->btsFlags & BTS_FAST_SECURE ){
72600 int iOff;
72601
 
 
72602 iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
72603 if( (iOff+szNew[i])>(int)pBt->usableSize ){
72604 rc = SQLITE_CORRUPT_BKPT;
72605 memset(apOld, 0, (i+1)*sizeof(MemPage*));
72606 goto balance_cleanup;
72607 }else{
72608 memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
72609 apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
72610 }
72611 }
72612 dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc);
@@ -87386,11 +87412,11 @@
87386 case OP_ChngCntRow: {
87387 assert( pOp->p2==1 );
87388 if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
87389 goto abort_due_to_error;
87390 }
87391 /* Fall through to the next case, OP_String */
87392 /* no break */ deliberate_fall_through
87393 }
87394
87395 /* Opcode: ResultRow P1 P2 * * *
87396 ** Synopsis: output=r[P1@P2]
@@ -97963,11 +97989,10 @@
97963 struct MemJournal {
97964 const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
97965 int nChunkSize; /* In-memory chunk-size */
97966
97967 int nSpill; /* Bytes of data before flushing */
97968 int nSize; /* Bytes of data currently in memory */
97969 FileChunk *pFirst; /* Head of in-memory chunk-list */
97970 FilePoint endpoint; /* Pointer to the end of the file */
97971 FilePoint readpoint; /* Pointer to the end of the last xRead() */
97972
97973 int flags; /* xOpen flags */
@@ -98024,18 +98049,17 @@
98024 }
98025
98026 /*
98027 ** Free the list of FileChunk structures headed at MemJournal.pFirst.
98028 */
98029 static void memjrnlFreeChunks(MemJournal *p){
98030 FileChunk *pIter;
98031 FileChunk *pNext;
98032 for(pIter=p->pFirst; pIter; pIter=pNext){
98033 pNext = pIter->pNext;
98034 sqlite3_free(pIter);
98035 }
98036 p->pFirst = 0;
98037 }
98038
98039 /*
98040 ** Flush the contents of memory to a real file on disk.
98041 */
@@ -98058,11 +98082,11 @@
98058 if( rc ) break;
98059 iOff += nChunk;
98060 }
98061 if( rc==SQLITE_OK ){
98062 /* No error has occurred. Free the in-memory buffers. */
98063 memjrnlFreeChunks(&copy);
98064 }
98065 }
98066 if( rc!=SQLITE_OK ){
98067 /* If an error occurred while creating or writing to the file, restore
98068 ** the original before returning. This way, SQLite uses the in-memory
@@ -98141,43 +98165,50 @@
98141 memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
98142 zWrite += iSpace;
98143 nWrite -= iSpace;
98144 p->endpoint.iOffset += iSpace;
98145 }
98146 p->nSize = iAmt + iOfst;
98147 }
98148 }
98149
98150 return SQLITE_OK;
98151 }
98152
98153 /*
98154 ** Truncate the file.
98155 **
98156 ** If the journal file is already on disk, truncate it there. Or, if it
98157 ** is still in main memory but is being truncated to zero bytes in size,
98158 ** ignore
98159 */
98160 static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
98161 MemJournal *p = (MemJournal *)pJfd;
98162 if( ALWAYS(size==0) ){
98163 memjrnlFreeChunks(p);
98164 p->nSize = 0;
98165 p->endpoint.pChunk = 0;
98166 p->endpoint.iOffset = 0;
98167 p->readpoint.pChunk = 0;
98168 p->readpoint.iOffset = 0;
98169 }
 
 
 
 
 
 
 
 
 
 
 
 
98170 return SQLITE_OK;
98171 }
98172
98173 /*
98174 ** Close the file.
98175 */
98176 static int memjrnlClose(sqlite3_file *pJfd){
98177 MemJournal *p = (MemJournal *)pJfd;
98178 memjrnlFreeChunks(p);
98179 return SQLITE_OK;
98180 }
98181
98182 /*
98183 ** Sync the file.
@@ -98992,10 +99023,11 @@
98992 /* IMP: R-51414-32910 */
98993 iCol = -1;
98994 }
98995 if( iCol<pTab->nCol ){
98996 cnt++;
 
98997 #ifndef SQLITE_OMIT_UPSERT
98998 if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){
98999 testcase( iCol==(-1) );
99000 if( IN_RENAME_OBJECT ){
99001 pExpr->iColumn = iCol;
@@ -99010,12 +99042,12 @@
99010 #endif /* SQLITE_OMIT_UPSERT */
99011 {
99012 pExpr->y.pTab = pTab;
99013 if( pParse->bReturning ){
99014 eNewExprOp = TK_REGISTER;
99015 pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable
99016 + iCol + 1;
99017 }else{
99018 pExpr->iColumn = (i16)iCol;
99019 eNewExprOp = TK_TRIGGER;
99020 #ifndef SQLITE_OMIT_TRIGGER
99021 if( iCol<0 ){
@@ -99211,15 +99243,17 @@
99211 pExpr->op = eNewExprOp;
99212 ExprSetProperty(pExpr, EP_Leaf);
99213 lookupname_end:
99214 if( cnt==1 ){
99215 assert( pNC!=0 );
 
99216 if( pParse->db->xAuth
99217 && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
99218 ){
99219 sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
99220 }
 
99221 /* Increment the nRef value on all name contexts from TopNC up to
99222 ** the point where the name matched. */
99223 for(;;){
99224 assert( pTopNC!=0 );
99225 pTopNC->nRef++;
@@ -99359,10 +99393,51 @@
99359 pExpr->iTable = pItem->iCursor;
99360 pExpr->iColumn--;
99361 pExpr->affExpr = SQLITE_AFF_INTEGER;
99362 break;
99363 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99364
99365 /* A column name: ID
99366 ** Or table name and column name: ID.ID
99367 ** Or a database, table and column: ID.ID.ID
99368 **
@@ -99587,10 +99662,11 @@
99587 if( pWin ){
99588 Select *pSel = pNC->pWinSelect;
99589 assert( pWin==pExpr->y.pWin );
99590 if( IN_RENAME_OBJECT==0 ){
99591 sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef);
 
99592 }
99593 sqlite3WalkExprList(pWalker, pWin->pPartition);
99594 sqlite3WalkExprList(pWalker, pWin->pOrderBy);
99595 sqlite3WalkExpr(pWalker, pWin->pFilter);
99596 sqlite3WindowLink(pSel, pWin);
@@ -99661,11 +99737,11 @@
99661 case TK_ISNOT: {
99662 Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
99663 assert( !ExprHasProperty(pExpr, EP_Reduced) );
99664 /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
99665 ** and "x IS NOT FALSE". */
99666 if( pRight && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){
99667 int rc = resolveExprStep(pWalker, pRight);
99668 if( rc==WRC_Abort ) return WRC_Abort;
99669 if( pRight->op==TK_TRUEFALSE ){
99670 pExpr->op2 = pExpr->op;
99671 pExpr->op = TK_TRUTH;
@@ -100164,29 +100240,28 @@
100164 /* Recursively resolve names in all subqueries
100165 */
100166 for(i=0; i<p->pSrc->nSrc; i++){
100167 SrcItem *pItem = &p->pSrc->a[i];
100168 if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
100169 NameContext *pNC; /* Used to iterate name contexts */
100170 int nRef = 0; /* Refcount for pOuterNC and outer contexts */
100171 const char *zSavedContext = pParse->zAuthContext;
100172
100173 /* Count the total number of references to pOuterNC and all of its
100174 ** parent contexts. After resolving references to expressions in
100175 ** pItem->pSelect, check if this value has changed. If so, then
100176 ** SELECT statement pItem->pSelect must be correlated. Set the
100177 ** pItem->fg.isCorrelated flag if this is the case. */
100178 for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
100179
100180 if( pItem->zName ) pParse->zAuthContext = pItem->zName;
100181 sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
100182 pParse->zAuthContext = zSavedContext;
100183 if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
100184
100185 for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
100186 assert( pItem->fg.isCorrelated==0 && nRef<=0 );
100187 pItem->fg.isCorrelated = (nRef!=0);
 
 
 
 
 
 
 
100188 }
100189 }
100190
100191 /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
100192 ** resolve the result-set expression list.
@@ -103131,11 +103206,11 @@
103131 assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
103132 pTab = p->pSrc->a[0].pTab;
103133
103134 /* Code an OP_Transaction and OP_TableLock for <table>. */
103135 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
103136 assert( iDb>=0 && iDb<SQLITE_MAX_ATTACHED );
103137 sqlite3CodeVerifySchema(pParse, iDb);
103138 sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
103139
103140 assert(v); /* sqlite3GetVdbe() has always been previously called */
103141 if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
@@ -107891,13 +107966,12 @@
107891 }
107892 if( rc==SQLITE_OK ){
107893 rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
107894 }
107895 assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
107896 if( pStep->pUpsert ){
107897 Upsert *pUpsert = pStep->pUpsert;
107898 assert( rc==SQLITE_OK );
107899 pUpsert->pUpsertSrc = pSrc;
107900 sNC.uNC.pUpsert = pUpsert;
107901 sNC.ncFlags = NC_UUpsert;
107902 rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
107903 if( rc==SQLITE_OK ){
@@ -108453,14 +108527,15 @@
108453 #ifndef SQLITE_OMIT_AUTHORIZATION
108454 sqlite3_xauth xAuth = db->xAuth;
108455 db->xAuth = 0;
108456 #endif
108457
 
108458 rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
108459 if( rc!=SQLITE_OK ) goto drop_column_done;
108460 pTab = sParse.pNewTable;
108461 if( pTab->nCol==1 || iCol>=pTab->nCol ){
108462 /* This can happen if the sqlite_schema table is corrupt */
108463 rc = SQLITE_CORRUPT_BKPT;
108464 goto drop_column_done;
108465 }
108466
@@ -112764,10 +112839,11 @@
112764 pParse->u1.pReturning = pRet;
112765 pRet->pParse = pParse;
112766 pRet->pReturnEL = pList;
112767 sqlite3ParserAddCleanup(pParse,
112768 (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
 
112769 if( db->mallocFailed ) return;
112770 pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
112771 pRet->retTrig.op = TK_RETURNING;
112772 pRet->retTrig.tr_tm = TRIGGER_AFTER;
112773 pRet->retTrig.bReturning = 1;
@@ -114298,10 +114374,11 @@
114298 ** the column names from the SELECT statement that defines the view.
114299 */
114300 assert( pTable->aCol==0 );
114301 pTable->nCol = pSelTab->nCol;
114302 pTable->aCol = pSelTab->aCol;
 
114303 pSelTab->nCol = 0;
114304 pSelTab->aCol = 0;
114305 assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
114306 }
114307 pTable->nNVCol = pTable->nCol;
@@ -116301,11 +116378,11 @@
116301 ** later, by sqlite3FinishCoding().
116302 */
116303 static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){
116304 assert( iDb>=0 && iDb<pToplevel->db->nDb );
116305 assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 );
116306 assert( iDb<SQLITE_MAX_ATTACHED+2 );
116307 assert( sqlite3SchemaMutexHeld(pToplevel->db, iDb, 0) );
116308 if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
116309 DbMaskSet(pToplevel->cookieMask, iDb);
116310 if( !OMIT_TEMPDB && iDb==1 ){
116311 sqlite3OpenTempDatabase(pToplevel);
@@ -117505,14 +117582,16 @@
117505 }
117506
117507 /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
117508 ** and the SELECT subtree. */
117509 pSrc->a[0].pTab = 0;
117510 pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
117511 pSrc->a[0].pTab = pTab;
117512 if( pSrc->a[0].fg.isIndexedBy ){
117513 pSrc->a[0].u2.pIBIndex = 0;
 
 
117514 }else if( pSrc->a[0].fg.isCte ){
117515 pSrc->a[0].u2.pCteUse->nUse++;
117516 }
117517
117518 /* generate the SELECT expression tree. */
@@ -129355,11 +129434,11 @@
129355 ** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate
129356 **
129357 ** Checkpoint the database.
129358 */
129359 case PragTyp_WAL_CHECKPOINT: {
129360 int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
129361 int eMode = SQLITE_CHECKPOINT_PASSIVE;
129362 if( zRight ){
129363 if( sqlite3StrICmp(zRight, "full")==0 ){
129364 eMode = SQLITE_CHECKPOINT_FULL;
129365 }else if( sqlite3StrICmp(zRight, "restart")==0 ){
@@ -130571,17 +130650,25 @@
130571 ** for common cleanups that happen on most calls. But for less
130572 ** common cleanups, we save a single NULL-pointer comparison in
130573 ** sqlite3ParserReset(), which reduces the total CPU cycle count.
130574 **
130575 ** If a memory allocation error occurs, then the cleanup happens immediately.
130576 ** When eithr SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
130577 ** pParse->earlyCleanup flag is set in that case. Calling code show verify
130578 ** that test cases exist for which this happens, to guard against possible
130579 ** use-after-free errors following an OOM. The preferred way to do this is
130580 ** to immediately follow the call to this routine with:
130581 **
130582 ** testcase( pParse->earlyCleanup );
 
 
 
 
 
 
 
 
130583 */
130584 SQLITE_PRIVATE void *sqlite3ParserAddCleanup(
130585 Parse *pParse, /* Destroy when this Parser finishes */
130586 void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
130587 void *pPtr /* Pointer to object to be cleaned up */
@@ -131075,16 +131162,20 @@
131075 sqlite3ExprDelete(db, p->pWhere);
131076 sqlite3ExprListDelete(db, p->pGroupBy);
131077 sqlite3ExprDelete(db, p->pHaving);
131078 sqlite3ExprListDelete(db, p->pOrderBy);
131079 sqlite3ExprDelete(db, p->pLimit);
 
131080 #ifndef SQLITE_OMIT_WINDOWFUNC
131081 if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
131082 sqlite3WindowListDelete(db, p->pWinDefn);
131083 }
 
 
 
 
131084 #endif
131085 if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
131086 if( bFree ) sqlite3DbFreeNN(db, p);
131087 p = pPrior;
131088 bFree = 1;
131089 }
131090 }
@@ -131396,10 +131487,13 @@
131396 static void unsetJoinExpr(Expr *p, int iTable){
131397 while( p ){
131398 if( ExprHasProperty(p, EP_FromJoin)
131399 && (iTable<0 || p->iRightJoinTable==iTable) ){
131400 ExprClearProperty(p, EP_FromJoin);
 
 
 
131401 }
131402 if( p->op==TK_FUNCTION && p->x.pList ){
131403 int i;
131404 for(i=0; i<p->x.pList->nExpr; i++){
131405 unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
@@ -132373,11 +132467,11 @@
132373 }
132374
132375 /*
132376 ** Name of the connection operator, used for error messages.
132377 */
132378 static const char *selectOpName(int id){
132379 char *z;
132380 switch( id ){
132381 case TK_ALL: z = "UNION ALL"; break;
132382 case TK_INTERSECT: z = "INTERSECT"; break;
132383 case TK_EXCEPT: z = "EXCEPT"; break;
@@ -133592,16 +133686,12 @@
133592 assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
133593 assert( p->selFlags & SF_Compound );
133594 db = pParse->db;
133595 pPrior = p->pPrior;
133596 dest = *pDest;
133597 if( pPrior->pOrderBy || pPrior->pLimit ){
133598 sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
133599 pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op));
133600 rc = 1;
133601 goto multi_select_end;
133602 }
133603
133604 v = sqlite3GetVdbe(pParse);
133605 assert( v!=0 ); /* The VDBE already created by calling function */
133606
133607 /* Create the destination temporary table if necessary
@@ -133654,11 +133744,11 @@
133654 assert( !pPrior->pLimit );
133655 pPrior->iLimit = p->iLimit;
133656 pPrior->iOffset = p->iOffset;
133657 pPrior->pLimit = p->pLimit;
133658 rc = sqlite3Select(pParse, pPrior, &dest);
133659 p->pLimit = 0;
133660 if( rc ){
133661 goto multi_select_end;
133662 }
133663 p->pPrior = 0;
133664 p->iLimit = pPrior->iLimit;
@@ -133675,12 +133765,12 @@
133675 rc = sqlite3Select(pParse, p, &dest);
133676 testcase( rc!=SQLITE_OK );
133677 pDelete = p->pPrior;
133678 p->pPrior = pPrior;
133679 p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
133680 if( pPrior->pLimit
133681 && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
133682 && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
133683 ){
133684 p->nSelectRow = sqlite3LogEst((u64)nLimit);
133685 }
133686 if( addr ){
@@ -133740,11 +133830,11 @@
133740 p->pPrior = 0;
133741 pLimit = p->pLimit;
133742 p->pLimit = 0;
133743 uniondest.eDest = op;
133744 ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
133745 selectOpName(p->op)));
133746 rc = sqlite3Select(pParse, p, &uniondest);
133747 testcase( rc!=SQLITE_OK );
133748 assert( p->pOrderBy==0 );
133749 pDelete = p->pPrior;
133750 p->pPrior = pPrior;
@@ -133816,11 +133906,11 @@
133816 p->pPrior = 0;
133817 pLimit = p->pLimit;
133818 p->pLimit = 0;
133819 intersectdest.iSDParm = tab2;
133820 ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
133821 selectOpName(p->op)));
133822 rc = sqlite3Select(pParse, p, &intersectdest);
133823 testcase( rc!=SQLITE_OK );
133824 pDelete = p->pPrior;
133825 p->pPrior = pPrior;
133826 if( p->nSelectRow>pPrior->nSelectRow ){
@@ -133925,11 +134015,12 @@
133925 SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
133926 if( p->selFlags & SF_Values ){
133927 sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
133928 }else{
133929 sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
133930 " do not have the same number of result columns", selectOpName(p->op));
 
133931 }
133932 }
133933
133934 /*
133935 ** Code an output subroutine for a coroutine implementation of a
@@ -134022,14 +134113,12 @@
134022 ** store the results in the appropriate memory cell and break out
134023 ** of the scan loop. Note that the select might return multiple columns
134024 ** if it is the RHS of a row-value IN operator.
134025 */
134026 case SRT_Mem: {
134027 if( pParse->nErr==0 ){
134028 testcase( pIn->nSdst>1 );
134029 sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
134030 }
134031 /* The LIMIT clause will jump out of the loop for us */
134032 break;
134033 }
134034 #endif /* #ifndef SQLITE_OMIT_SUBQUERY */
134035
@@ -134317,11 +134406,11 @@
134317 regOutA = ++pParse->nMem;
134318 regOutB = ++pParse->nMem;
134319 sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
134320 sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
134321
134322 ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op)));
134323
134324 /* Generate a coroutine to evaluate the SELECT statement to the
134325 ** left of the compound operator - the "A" select.
134326 */
134327 addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
@@ -135479,10 +135568,39 @@
135479 }
135480 }while( x.nChng );
135481 return nChng;
135482 }
135483
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135484 #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
135485 /*
135486 ** Make copies of relevant WHERE clause terms of the outer query into
135487 ** the WHERE clause of subquery. Example:
135488 **
@@ -135526,13 +135644,24 @@
135526 **
135527 ** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9).
135528 ** But if the (b2=2) term were to be pushed down into the bb subquery,
135529 ** then the (1,1,NULL) row would be suppressed.
135530 **
135531 ** (6) The inner query features one or more window-functions (since
135532 ** changes to the WHERE clause of the inner query could change the
135533 ** window over which window functions are calculated).
 
 
 
 
 
 
 
 
 
 
 
135534 **
135535 ** (7) The inner query is a Common Table Expression (CTE) that should
135536 ** be materialized. (This restriction is implemented in the calling
135537 ** routine.)
135538 **
@@ -135546,17 +135675,21 @@
135546 int iCursor, /* Cursor number of the subquery */
135547 int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
135548 ){
135549 Expr *pNew;
135550 int nChng = 0;
135551 Select *pSel;
135552 if( pWhere==0 ) return 0;
135553 if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */
135554
135555 #ifndef SQLITE_OMIT_WINDOWFUNC
135556 for(pSel=pSubq; pSel; pSel=pSel->pPrior){
135557 if( pSel->pWin ) return 0; /* restriction (6) */
 
 
 
 
 
135558 }
135559 #endif
135560
135561 #ifdef SQLITE_DEBUG
135562 /* Only the first term of a compound can have a WITH clause. But make
@@ -135599,10 +135732,18 @@
135599 x.iTable = iCursor;
135600 x.iNewTable = iCursor;
135601 x.isLeftJoin = 0;
135602 x.pEList = pSubq->pEList;
135603 pNew = substExpr(&x, pNew);
 
 
 
 
 
 
 
 
135604 if( pSubq->selFlags & SF_Aggregate ){
135605 pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
135606 }else{
135607 pSubq->pWhere = sqlite3ExprAnd(pParse, pSubq->pWhere, pNew);
135608 }
@@ -136190,11 +136331,14 @@
136190 if( IsVirtual(pTab) || pTab->pSelect ){
136191 i16 nCol;
136192 u8 eCodeOrig = pWalker->eCode;
136193 if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
136194 assert( pFrom->pSelect==0 );
136195 if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){
 
 
 
136196 sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
136197 pTab->zName);
136198 }
136199 #ifndef SQLITE_OMIT_VIRTUALTABLE
136200 if( IsVirtual(pTab)
@@ -136969,12 +137113,23 @@
136969 if( IgnorableDistinct(pDest) ){
136970 assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
136971 pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
136972 pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo );
136973 /* All of these destinations are also able to ignore the ORDER BY clause */
136974 sqlite3ExprListDelete(db, p->pOrderBy);
136975 p->pOrderBy = 0;
 
 
 
 
 
 
 
 
 
 
 
136976 p->selFlags &= ~SF_Distinct;
136977 p->selFlags |= SF_NoopOrderBy;
136978 }
136979 sqlite3SelectPrep(pParse, p, 0);
136980 if( pParse->nErr || db->mallocFailed ){
@@ -137589,10 +137744,11 @@
137589 */
137590 pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
137591 if( pAggInfo ){
137592 sqlite3ParserAddCleanup(pParse,
137593 (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
 
137594 }
137595 if( db->mallocFailed ){
137596 goto select_end;
137597 }
137598 pAggInfo->selId = p->selId;
@@ -147345,11 +147501,14 @@
147345 sqlite3 *db = pParse->db;
147346
147347 assert( pExpr->op==TK_EXISTS );
147348 assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
147349
147350 if( (pSel->selFlags & SF_Aggregate) || pSel->pWin ) return;
 
 
 
147351 if( pSel->pPrior ) return;
147352 if( pSel->pWhere==0 ) return;
147353 if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;
147354
147355 pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -147542,10 +147701,16 @@
147542 pNew->u.x.leftColumn = aiCurCol[1];
147543 testcase( (prereqLeft | extraRight) != prereqLeft );
147544 pNew->prereqRight = prereqLeft | extraRight;
147545 pNew->prereqAll = prereqAll;
147546 pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
 
 
 
 
 
 
147547 }
147548 }
147549
147550 #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
147551 /* If a term is the BETWEEN operator, create two new virtual terms
@@ -153019,11 +153184,11 @@
153019 }
153020 }
153021 if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
153022 pWInfo->revMask = ALLBITS;
153023 }
153024 if( pParse->nErr || NEVER(db->mallocFailed) ){
153025 goto whereBeginError;
153026 }
153027 #ifdef WHERETRACE_ENABLED
153028 if( sqlite3WhereTrace ){
153029 sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
@@ -154961,19 +155126,23 @@
154961 ** to be processed as part of SELECT statement pSel). The window is linked
154962 ** in if either (a) there are no other windows already linked to this
154963 ** SELECT, or (b) the windows already linked use a compatible window frame.
154964 */
154965 SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){
154966 if( pSel!=0
154967 && (0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0))
154968 ){
154969 pWin->pNextWin = pSel->pWin;
154970 if( pSel->pWin ){
154971 pSel->pWin->ppThis = &pWin->pNextWin;
154972 }
154973 pSel->pWin = pWin;
154974 pWin->ppThis = &pSel->pWin;
 
 
 
 
154975 }
154976 }
154977
154978 /*
154979 ** Return 0 if the two window objects are identical, 1 if they are
@@ -155718,10 +155887,11 @@
155718 int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */
155719 int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */
155720 int regString = ++pParse->nMem; /* Reg. for constant value '' */
155721 int arith = OP_Add; /* OP_Add or OP_Subtract */
155722 int addrGe; /* Jump destination */
 
155723
155724 assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
155725 assert( pOrderBy && pOrderBy->nExpr==1 );
155726 if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
155727 switch( op ){
@@ -155808,10 +155978,12 @@
155808
155809 /* Compare registers reg2 and reg1, taking the jump if required. Note that
155810 ** control skips over this test if the BIGNULL flag is set and either
155811 ** reg1 or reg2 contain a NULL value. */
155812 sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
 
 
155813 sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
155814
155815 assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
155816 testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
155817 testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
@@ -156814,15 +156986,25 @@
156814 ** SQLITE_LIMIT_COMPOUND_SELECT.
156815 */
156816 static void parserDoubleLinkSelect(Parse *pParse, Select *p){
156817 assert( p!=0 );
156818 if( p->pPrior ){
156819 Select *pNext = 0, *pLoop;
156820 int mxSelect, cnt = 0;
156821 for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){
156822 pLoop->pNext = pNext;
156823 pLoop->selFlags |= SF_Compound;
 
 
 
 
 
 
 
 
 
 
156824 }
156825 if( (p->selFlags & SF_MultiValue)==0 &&
156826 (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 &&
156827 cnt>mxSelect
156828 ){
@@ -166129,11 +166311,11 @@
166129 ){
166130 #ifdef SQLITE_OMIT_WAL
166131 return SQLITE_OK;
166132 #else
166133 int rc; /* Return code */
166134 int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
166135
166136 #ifdef SQLITE_ENABLE_API_ARMOR
166137 if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
166138 #endif
166139
@@ -166152,10 +166334,12 @@
166152 }
166153
166154 sqlite3_mutex_enter(db->mutex);
166155 if( zDb && zDb[0] ){
166156 iDb = sqlite3FindDbName(db, zDb);
 
 
166157 }
166158 if( iDb<0 ){
166159 rc = SQLITE_ERROR;
166160 sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
166161 }else{
@@ -166200,11 +166384,11 @@
166200 **
166201 ** The mutex on database handle db should be held by the caller. The mutex
166202 ** associated with the specific b-tree being checkpointed is taken by
166203 ** this function while the checkpoint is running.
166204 **
166205 ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are
166206 ** checkpointed. If an error is encountered it is returned immediately -
166207 ** no attempt is made to checkpoint any remaining databases.
166208 **
166209 ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART
166210 ** or TRUNCATE.
@@ -166215,13 +166399,15 @@
166215 int bBusy = 0; /* True if SQLITE_BUSY has been encountered */
166216
166217 assert( sqlite3_mutex_held(db->mutex) );
166218 assert( !pnLog || *pnLog==-1 );
166219 assert( !pnCkpt || *pnCkpt==-1 );
 
 
166220
166221 for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
166222 if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
166223 rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
166224 pnLog = 0;
166225 pnCkpt = 0;
166226 if( rc==SQLITE_BUSY ){
166227 bBusy = 1;
@@ -174914,13 +175100,13 @@
174914 res = fts3PoslistNearMerge(
174915 &pOut, aTmp, nParam1, nParam2, paPoslist, &p2
174916 );
174917 if( res ){
174918 nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
174919 if( nNew>=0 ){
 
174920 assert( pPhrase->doclist.pList[nNew]=='\0' );
174921 assert( nNew<=pPhrase->doclist.nList && nNew>0 );
174922 memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
174923 pPhrase->doclist.nList = nNew;
174924 }
174925 *paPoslist = pPhrase->doclist.pList;
174926 *pnToken = pPhrase->nToken;
@@ -176850,10 +177036,15 @@
176850
176851 if( sqlite3_fts3_enable_parentheses ){
176852 if( *zInput=='(' ){
176853 int nConsumed = 0;
176854 pParse->nNest++;
 
 
 
 
 
176855 rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
176856 *pnConsumed = (int)(zInput - z) + 1 + nConsumed;
176857 return rc;
176858 }else if( *zInput==')' ){
176859 pParse->nNest--;
@@ -184253,31 +184444,30 @@
184253 if( pNode->block.a){
184254 rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
184255 while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
184256 blobGrowBuffer(&pNode->key, reader.term.n, &rc);
184257 if( rc==SQLITE_OK ){
184258 if( reader.term.n<=0 ){
184259 rc = FTS_CORRUPT_VTAB;
184260 }else{
184261 memcpy(pNode->key.a, reader.term.a, reader.term.n);
184262 pNode->key.n = reader.term.n;
184263 if( i>0 ){
184264 char *aBlock = 0;
184265 int nBlock = 0;
184266 pNode = &pWriter->aNodeWriter[i-1];
184267 pNode->iBlock = reader.iChild;
184268 rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
184269 blobGrowBuffer(&pNode->block,
184270 MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
184271 );
184272 if( rc==SQLITE_OK ){
184273 memcpy(pNode->block.a, aBlock, nBlock);
184274 pNode->block.n = nBlock;
184275 memset(&pNode->block.a[nBlock], 0, FTS3_NODE_PADDING);
184276 }
184277 sqlite3_free(aBlock);
184278 }
184279 }
184280 }
184281 }
184282 nodeReaderRelease(&reader);
184283 }
@@ -187755,10 +187945,11 @@
187755 memset(pCsr, 0, sizeof(unicode_cursor));
187756
187757 pCsr->aInput = (const unsigned char *)aInput;
187758 if( aInput==0 ){
187759 pCsr->nInput = 0;
 
187760 }else if( nInput<0 ){
187761 pCsr->nInput = (int)strlen(aInput);
187762 }else{
187763 pCsr->nInput = nInput;
187764 }
@@ -224323,21 +224514,26 @@
224323 pIter->nPoslist = ((int)(p[0])) >> 1;
224324 pIter->nSize = 1;
224325 }
224326
224327 pIter->aPoslist = p;
 
 
 
224328 }
224329 }
224330
224331 static void fts5DoclistIterInit(
224332 Fts5Buffer *pBuf,
224333 Fts5DoclistIter *pIter
224334 ){
224335 memset(pIter, 0, sizeof(*pIter));
224336 pIter->aPoslist = pBuf->p;
224337 pIter->aEof = &pBuf->p[pBuf->n];
224338 fts5DoclistIterNext(pIter);
 
 
224339 }
224340
224341 #if 0
224342 /*
224343 ** Append a doclist to buffer pBuf.
@@ -225095,12 +225291,13 @@
225095 ** Return the current term.
225096 */
225097 static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
225098 int n;
225099 const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
 
225100 *pn = n-1;
225101 return &z[1];
225102 }
225103
225104 /*
225105 ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
225106 */
@@ -228382,11 +228579,12 @@
228382 ){
228383 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
228384 int n;
228385 int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
228386 if( rc==SQLITE_OK ){
228387 pIter->b = &pIter->a[n];
 
228388 *piCol = 0;
228389 *piOff = 0;
228390 fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
228391 }
228392 return rc;
@@ -228441,19 +228639,21 @@
228441 pIter->a = &pSorter->aPoslist[i1];
228442 }else{
228443 rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
228444 }
228445 if( rc==SQLITE_OK ){
228446 pIter->b = &pIter->a[n];
 
228447 *piCol = 0;
228448 fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
228449 }
228450 }else{
228451 int n;
228452 rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
228453 if( rc==SQLITE_OK ){
228454 pIter->b = &pIter->a[n];
 
228455 if( n<=0 ){
228456 *piCol = -1;
228457 }else if( pIter->a[0]==0x01 ){
228458 pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
228459 }else{
@@ -228927,11 +229127,11 @@
228927 assert( nArg>0 );
228928 rc = SQLITE_ERROR;
228929 *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
228930 }else{
228931 rc = pMod->x.xCreate(
228932 pMod->pUserData, &azArg[1], (nArg?nArg-1:0), &pConfig->pTok
228933 );
228934 pConfig->pTokApi = &pMod->x;
228935 if( rc!=SQLITE_OK ){
228936 if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
228937 }else{
@@ -228990,11 +229190,11 @@
228990 int nArg, /* Number of args */
228991 sqlite3_value **apUnused /* Function arguments */
228992 ){
228993 assert( nArg==0 );
228994 UNUSED_PARAM2(nArg, apUnused);
228995 sqlite3_result_text(pCtx, "fts5: 2021-02-22 11:07:25 b66a49570852cf118a372a6ac44be3070cf9b4254696f16315b7c79a614e6c35", -1, SQLITE_TRANSIENT);
228996 }
228997
228998 /*
228999 ** Return true if zName is the extension on one of the shadow tables used
229000 ** by this module.
@@ -233916,12 +234116,12 @@
233916 }
233917 #endif /* SQLITE_CORE */
233918 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
233919
233920 /************** End of stmt.c ************************************************/
233921 #if __LINE__!=233921
233922 #undef SQLITE_SOURCE_ID
233923 #define SQLITE_SOURCE_ID "2021-02-22 16:42:09 b5a0778cc5a98a864bea72670f83262da940aceb91fa4cdf46ec097337a3alt2"
233924 #endif
233925 /* Return the source-id for this library */
233926 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
233927 /************************** End of sqlite3.c ******************************/
233928
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1186,11 +1186,11 @@
1186 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1187 ** [sqlite_version()] and [sqlite_source_id()].
1188 */
1189 #define SQLITE_VERSION "3.35.0"
1190 #define SQLITE_VERSION_NUMBER 3035000
1191 #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
1192
1193 /*
1194 ** CAPI3REF: Run-Time Library Version Numbers
1195 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1196 **
@@ -3193,11 +3193,17 @@
3193 ** The first argument is an integer which is 0 to disable views,
3194 ** positive to enable views or negative to leave the setting unchanged.
3195 ** The second parameter is a pointer to an integer into which
3196 ** is written 0 or 1 to indicate whether views are disabled or enabled
3197 ** following this call. The second parameter may be a NULL pointer, in
3198 ** which case the view setting is not reported back.
3199 **
3200 ** <p>Originally this option disabled all views. ^(However, since
3201 ** SQLite version 3.35.0, TEMP views are still allowed even if
3202 ** this option is off. So, in other words, this option now only disables
3203 ** views in the main database schema or in the schemas of ATTACH-ed
3204 ** databases.)^ </dd>
3205 **
3206 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
3207 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
3208 ** <dd> ^This option is used to enable or disable the
3209 ** [fts3_tokenizer()] function which is part of the
@@ -11617,22 +11623,27 @@
11623 ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
11624 ** created by [sqlite3changeset_start()]. In the latter case, the most recent
11625 ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
11626 ** is not the case, this function returns [SQLITE_MISUSE].
11627 **
11628 ** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
11629 ** outputs are set through these pointers:
11630 **
11631 ** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
11632 ** depending on the type of change that the iterator currently points to;
11633 **
11634 ** *pnCol is set to the number of columns in the table affected by the change; and
11635 **
11636 ** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
11637 ** the name of the table affected by the current change. The buffer remains
11638 ** valid until either sqlite3changeset_next() is called on the iterator
11639 ** or until the conflict-handler function returns.
11640 **
11641 ** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
11642 ** is an indirect change, or false (0) otherwise. See the documentation for
11643 ** [sqlite3session_indirect()] for a description of direct and indirect
11644 ** changes.
 
 
11645 **
11646 ** If no error occurs, SQLITE_OK is returned. If an error does occur, an
11647 ** SQLite error code is returned. The values of the output variables may not
11648 ** be trusted in this case.
11649 */
@@ -15368,11 +15379,11 @@
15379
15380 /* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
15381 #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
15382 #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
15383 #define BTREE_APPEND 0x08 /* Insert is likely an append */
15384 #define BTREE_PREFORMAT 0x80 /* Inserted data is a preformated cell */
15385
15386 /* An instance of the BtreePayload object describes the content of a single
15387 ** entry in either an index or table btree.
15388 **
15389 ** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
@@ -16942,10 +16953,15 @@
16953 #define SQLITE_TRACE_LEGACY 0
16954 #define SQLITE_TRACE_XPROFILE 0
16955 #endif /* SQLITE_OMIT_DEPRECATED */
16956 #define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */
16957
16958 /*
16959 ** Maximum number of sqlite3.aDb[] entries. This is the number of attached
16960 ** databases plus 2 for "main" and "temp".
16961 */
16962 #define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2)
16963
16964 /*
16965 ** Each database connection is an instance of the following structure.
16966 */
16967 struct sqlite3 {
@@ -18665,10 +18681,11 @@
18681 #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */
18682 #define SF_View 0x0200000 /* SELECT statement is a view */
18683 #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
18684 #define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
18685 #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
18686 #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
18687
18688 /*
18689 ** The results of a SELECT can be distributed in several ways, as defined
18690 ** by one of the following macros. The "SRT" prefix means "SELECT Result
18691 ** Type".
@@ -20255,10 +20272,11 @@
20272 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
20273 SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
20274 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
20275 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
20276 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
20277 SQLITE_PRIVATE const char *sqlite3SelectOpName(int);
20278 SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*);
20279
20280 #ifdef SQLITE_DEBUG
20281 SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
20282 #endif
@@ -29329,11 +29347,11 @@
29347 setStrAccumError(p, SQLITE_TOOBIG);
29348 return p->nAlloc - p->nChar - 1;
29349 }else{
29350 char *zOld = isMalloced(p) ? p->zText : 0;
29351 i64 szNew = p->nChar;
29352 szNew += (sqlite3_int64)N + 1;
29353 if( szNew+p->nChar<=p->mxAlloc ){
29354 /* Force exponential buffer size growth as long as it does not overflow,
29355 ** to avoid having to call this routine too often */
29356 szNew += p->nChar;
29357 }
@@ -50636,10 +50654,11 @@
50654 #endif
50655 p->page.pBuf = pPg;
50656 p->page.pExtra = &p[1];
50657 p->isBulkLocal = 0;
50658 p->isAnchor = 0;
50659 p->pLruPrev = 0; /* Initializing this saves a valgrind error */
50660 }
50661 (*pCache->pnPurgeable)++;
50662 return p;
50663 }
50664
@@ -52554,10 +52573,11 @@
52573 i64 iOffset; /* Starting offset in main journal */
52574 i64 iHdrOffset; /* See above */
52575 Bitvec *pInSavepoint; /* Set of pages in this savepoint */
52576 Pgno nOrig; /* Original number of pages in file */
52577 Pgno iSubRec; /* Index of first record in sub-journal */
52578 int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */
52579 #ifndef SQLITE_OMIT_WAL
52580 u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
52581 #endif
52582 };
52583
@@ -53189,10 +53209,13 @@
53209 Pgno pgno = pPg->pgno;
53210 int i;
53211 for(i=0; i<pPager->nSavepoint; i++){
53212 p = &pPager->aSavepoint[i];
53213 if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
53214 for(i=i+1; i<pPager->nSavepoint; i++){
53215 pPager->aSavepoint[i].bTruncateOnRelease = 0;
53216 }
53217 return 1;
53218 }
53219 }
53220 return 0;
53221 }
@@ -58967,10 +58990,11 @@
58990 }else{
58991 aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
58992 }
58993 aNew[ii].iSubRec = pPager->nSubRec;
58994 aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
58995 aNew[ii].bTruncateOnRelease = 1;
58996 if( !aNew[ii].pInSavepoint ){
58997 return SQLITE_NOMEM_BKPT;
58998 }
58999 if( pagerUseWal(pPager) ){
59000 sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
@@ -59048,17 +59072,19 @@
59072 pPager->nSavepoint = nNew;
59073
59074 /* If this is a release of the outermost savepoint, truncate
59075 ** the sub-journal to zero bytes in size. */
59076 if( op==SAVEPOINT_RELEASE ){
59077 PagerSavepoint *pRel = &pPager->aSavepoint[nNew];
59078 if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){
59079 /* Only truncate if it is an in-memory sub-journal. */
59080 if( sqlite3JournalIsInMemory(pPager->sjfd) ){
59081 i64 sz = (pPager->pageSize+4)*pRel->iSubRec;
59082 rc = sqlite3OsTruncate(pPager->sjfd, sz);
59083 assert( rc==SQLITE_OK );
59084 }
59085 pPager->nSubRec = pRel->iSubRec;
59086 }
59087 }
59088 /* Else this is a rollback operation, playback the specified savepoint.
59089 ** If this is a temp-file, it is possible that the journal file has
59090 ** not yet been opened. In this case there have been no changes to
@@ -72558,11 +72584,13 @@
72584 }else{
72585 pRight = findCell(pParent, i+nxDiv-pParent->nOverflow);
72586 }
72587 pgno = get4byte(pRight);
72588 while( 1 ){
72589 if( rc==SQLITE_OK ){
72590 rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
72591 }
72592 if( rc ){
72593 memset(apOld, 0, (i+1)*sizeof(MemPage*));
72594 goto balance_cleanup;
72595 }
72596 if( apOld[i]->nFree<0 ){
@@ -72597,16 +72625,14 @@
72625 ** buffer. It will be copied out again as soon as the aSpace[] buffer
72626 ** is allocated. */
72627 if( pBt->btsFlags & BTS_FAST_SECURE ){
72628 int iOff;
72629
72630 /* If the following if() condition is not true, the db is corrupted.
72631 ** The call to dropCell() below will detect this. */
72632 iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
72633 if( (iOff+szNew[i])<=(int)pBt->usableSize ){
 
 
 
 
72634 memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
72635 apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
72636 }
72637 }
72638 dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc);
@@ -87386,11 +87412,11 @@
87412 case OP_ChngCntRow: {
87413 assert( pOp->p2==1 );
87414 if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
87415 goto abort_due_to_error;
87416 }
87417 /* Fall through to the next case, OP_ResultRow */
87418 /* no break */ deliberate_fall_through
87419 }
87420
87421 /* Opcode: ResultRow P1 P2 * * *
87422 ** Synopsis: output=r[P1@P2]
@@ -97963,11 +97989,10 @@
97989 struct MemJournal {
97990 const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
97991 int nChunkSize; /* In-memory chunk-size */
97992
97993 int nSpill; /* Bytes of data before flushing */
 
97994 FileChunk *pFirst; /* Head of in-memory chunk-list */
97995 FilePoint endpoint; /* Pointer to the end of the file */
97996 FilePoint readpoint; /* Pointer to the end of the last xRead() */
97997
97998 int flags; /* xOpen flags */
@@ -98024,18 +98049,17 @@
98049 }
98050
98051 /*
98052 ** Free the list of FileChunk structures headed at MemJournal.pFirst.
98053 */
98054 static void memjrnlFreeChunks(FileChunk *pFirst){
98055 FileChunk *pIter;
98056 FileChunk *pNext;
98057 for(pIter=pFirst; pIter; pIter=pNext){
98058 pNext = pIter->pNext;
98059 sqlite3_free(pIter);
98060 }
 
98061 }
98062
98063 /*
98064 ** Flush the contents of memory to a real file on disk.
98065 */
@@ -98058,11 +98082,11 @@
98082 if( rc ) break;
98083 iOff += nChunk;
98084 }
98085 if( rc==SQLITE_OK ){
98086 /* No error has occurred. Free the in-memory buffers. */
98087 memjrnlFreeChunks(copy.pFirst);
98088 }
98089 }
98090 if( rc!=SQLITE_OK ){
98091 /* If an error occurred while creating or writing to the file, restore
98092 ** the original before returning. This way, SQLite uses the in-memory
@@ -98141,43 +98165,50 @@
98165 memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
98166 zWrite += iSpace;
98167 nWrite -= iSpace;
98168 p->endpoint.iOffset += iSpace;
98169 }
 
98170 }
98171 }
98172
98173 return SQLITE_OK;
98174 }
98175
98176 /*
98177 ** Truncate the in-memory file.
 
 
 
 
98178 */
98179 static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
98180 MemJournal *p = (MemJournal *)pJfd;
98181 FileChunk *pIter = 0;
98182
98183 if( size==0 ){
98184 memjrnlFreeChunks(p->pFirst);
98185 p->pFirst = 0;
98186 }else{
98187 i64 iOff = p->nChunkSize;
98188 for(pIter=p->pFirst; ALWAYS(pIter) && iOff<=size; pIter=pIter->pNext){
98189 iOff += p->nChunkSize;
98190 }
98191 if( ALWAYS(pIter) ){
98192 memjrnlFreeChunks(pIter->pNext);
98193 pIter->pNext = 0;
98194 }
98195 }
98196
98197 p->endpoint.pChunk = pIter;
98198 p->endpoint.iOffset = size;
98199 p->readpoint.pChunk = 0;
98200 p->readpoint.iOffset = 0;
98201 return SQLITE_OK;
98202 }
98203
98204 /*
98205 ** Close the file.
98206 */
98207 static int memjrnlClose(sqlite3_file *pJfd){
98208 MemJournal *p = (MemJournal *)pJfd;
98209 memjrnlFreeChunks(p->pFirst);
98210 return SQLITE_OK;
98211 }
98212
98213 /*
98214 ** Sync the file.
@@ -98992,10 +99023,11 @@
99023 /* IMP: R-51414-32910 */
99024 iCol = -1;
99025 }
99026 if( iCol<pTab->nCol ){
99027 cnt++;
99028 pMatch = 0;
99029 #ifndef SQLITE_OMIT_UPSERT
99030 if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){
99031 testcase( iCol==(-1) );
99032 if( IN_RENAME_OBJECT ){
99033 pExpr->iColumn = iCol;
@@ -99010,12 +99042,12 @@
99042 #endif /* SQLITE_OMIT_UPSERT */
99043 {
99044 pExpr->y.pTab = pTab;
99045 if( pParse->bReturning ){
99046 eNewExprOp = TK_REGISTER;
99047 pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable +
99048 sqlite3TableColumnToStorage(pTab, iCol) + 1;
99049 }else{
99050 pExpr->iColumn = (i16)iCol;
99051 eNewExprOp = TK_TRIGGER;
99052 #ifndef SQLITE_OMIT_TRIGGER
99053 if( iCol<0 ){
@@ -99211,15 +99243,17 @@
99243 pExpr->op = eNewExprOp;
99244 ExprSetProperty(pExpr, EP_Leaf);
99245 lookupname_end:
99246 if( cnt==1 ){
99247 assert( pNC!=0 );
99248 #ifndef SQLITE_OMIT_AUTHORIZATION
99249 if( pParse->db->xAuth
99250 && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
99251 ){
99252 sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
99253 }
99254 #endif
99255 /* Increment the nRef value on all name contexts from TopNC up to
99256 ** the point where the name matched. */
99257 for(;;){
99258 assert( pTopNC!=0 );
99259 pTopNC->nRef++;
@@ -99359,10 +99393,51 @@
99393 pExpr->iTable = pItem->iCursor;
99394 pExpr->iColumn--;
99395 pExpr->affExpr = SQLITE_AFF_INTEGER;
99396 break;
99397 }
99398
99399 /* An optimization: Attempt to convert
99400 **
99401 ** "expr IS NOT NULL" --> "TRUE"
99402 ** "expr IS NULL" --> "FALSE"
99403 **
99404 ** if we can prove that "expr" is never NULL. Call this the
99405 ** "NOT NULL strength reduction optimization".
99406 **
99407 ** If this optimization occurs, also restore the NameContext ref-counts
99408 ** to the state they where in before the "column" LHS expression was
99409 ** resolved. This prevents "column" from being counted as having been
99410 ** referenced, which might prevent a SELECT from being erroneously
99411 ** marked as correlated.
99412 */
99413 case TK_NOTNULL:
99414 case TK_ISNULL: {
99415 int anRef[8];
99416 NameContext *p;
99417 int i;
99418 for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
99419 anRef[i] = p->nRef;
99420 }
99421 sqlite3WalkExpr(pWalker, pExpr->pLeft);
99422 if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
99423 if( pExpr->op==TK_NOTNULL ){
99424 pExpr->u.zToken = "true";
99425 ExprSetProperty(pExpr, EP_IsTrue);
99426 }else{
99427 pExpr->u.zToken = "false";
99428 ExprSetProperty(pExpr, EP_IsFalse);
99429 }
99430 pExpr->op = TK_TRUEFALSE;
99431 for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
99432 p->nRef = anRef[i];
99433 }
99434 sqlite3ExprDelete(pParse->db, pExpr->pLeft);
99435 pExpr->pLeft = 0;
99436 }
99437 return WRC_Prune;
99438 }
99439
99440 /* A column name: ID
99441 ** Or table name and column name: ID.ID
99442 ** Or a database, table and column: ID.ID.ID
99443 **
@@ -99587,10 +99662,11 @@
99662 if( pWin ){
99663 Select *pSel = pNC->pWinSelect;
99664 assert( pWin==pExpr->y.pWin );
99665 if( IN_RENAME_OBJECT==0 ){
99666 sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef);
99667 if( pParse->db->mallocFailed ) break;
99668 }
99669 sqlite3WalkExprList(pWalker, pWin->pPartition);
99670 sqlite3WalkExprList(pWalker, pWin->pOrderBy);
99671 sqlite3WalkExpr(pWalker, pWin->pFilter);
99672 sqlite3WindowLink(pSel, pWin);
@@ -99661,11 +99737,11 @@
99737 case TK_ISNOT: {
99738 Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
99739 assert( !ExprHasProperty(pExpr, EP_Reduced) );
99740 /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
99741 ** and "x IS NOT FALSE". */
99742 if( ALWAYS(pRight) && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){
99743 int rc = resolveExprStep(pWalker, pRight);
99744 if( rc==WRC_Abort ) return WRC_Abort;
99745 if( pRight->op==TK_TRUEFALSE ){
99746 pExpr->op2 = pExpr->op;
99747 pExpr->op = TK_TRUTH;
@@ -100164,29 +100240,28 @@
100240 /* Recursively resolve names in all subqueries
100241 */
100242 for(i=0; i<p->pSrc->nSrc; i++){
100243 SrcItem *pItem = &p->pSrc->a[i];
100244 if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
100245 int nRef = pOuterNC ? pOuterNC->nRef : 0;
 
100246 const char *zSavedContext = pParse->zAuthContext;
100247
 
 
 
 
 
 
 
100248 if( pItem->zName ) pParse->zAuthContext = pItem->zName;
100249 sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
100250 pParse->zAuthContext = zSavedContext;
100251 if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
100252
100253 /* If the number of references to the outer context changed when
100254 ** expressions in the sub-select were resolved, the sub-select
100255 ** is correlated. It is not required to check the refcount on any
100256 ** but the innermost outer context object, as lookupName() increments
100257 ** the refcount on all contexts between the current one and the
100258 ** context containing the column when it resolves a name. */
100259 if( pOuterNC ){
100260 assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
100261 pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
100262 }
100263 }
100264 }
100265
100266 /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
100267 ** resolve the result-set expression list.
@@ -103131,11 +103206,11 @@
103206 assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
103207 pTab = p->pSrc->a[0].pTab;
103208
103209 /* Code an OP_Transaction and OP_TableLock for <table>. */
103210 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
103211 assert( iDb>=0 && iDb<SQLITE_MAX_DB );
103212 sqlite3CodeVerifySchema(pParse, iDb);
103213 sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
103214
103215 assert(v); /* sqlite3GetVdbe() has always been previously called */
103216 if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
@@ -107891,13 +107966,12 @@
107966 }
107967 if( rc==SQLITE_OK ){
107968 rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
107969 }
107970 assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
107971 if( pStep->pUpsert && rc==SQLITE_OK ){
107972 Upsert *pUpsert = pStep->pUpsert;
 
107973 pUpsert->pUpsertSrc = pSrc;
107974 sNC.uNC.pUpsert = pUpsert;
107975 sNC.ncFlags = NC_UUpsert;
107976 rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
107977 if( rc==SQLITE_OK ){
@@ -108453,14 +108527,15 @@
108527 #ifndef SQLITE_OMIT_AUTHORIZATION
108528 sqlite3_xauth xAuth = db->xAuth;
108529 db->xAuth = 0;
108530 #endif
108531
108532 UNUSED_PARAMETER(NotUsed);
108533 rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
108534 if( rc!=SQLITE_OK ) goto drop_column_done;
108535 pTab = sParse.pNewTable;
108536 if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
108537 /* This can happen if the sqlite_schema table is corrupt */
108538 rc = SQLITE_CORRUPT_BKPT;
108539 goto drop_column_done;
108540 }
108541
@@ -112764,10 +112839,11 @@
112839 pParse->u1.pReturning = pRet;
112840 pRet->pParse = pParse;
112841 pRet->pReturnEL = pList;
112842 sqlite3ParserAddCleanup(pParse,
112843 (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
112844 testcase( pParse->earlyCleanup );
112845 if( db->mallocFailed ) return;
112846 pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
112847 pRet->retTrig.op = TK_RETURNING;
112848 pRet->retTrig.tr_tm = TRIGGER_AFTER;
112849 pRet->retTrig.bReturning = 1;
@@ -114298,10 +114374,11 @@
114374 ** the column names from the SELECT statement that defines the view.
114375 */
114376 assert( pTable->aCol==0 );
114377 pTable->nCol = pSelTab->nCol;
114378 pTable->aCol = pSelTab->aCol;
114379 pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT);
114380 pSelTab->nCol = 0;
114381 pSelTab->aCol = 0;
114382 assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
114383 }
114384 pTable->nNVCol = pTable->nCol;
@@ -116301,11 +116378,11 @@
116378 ** later, by sqlite3FinishCoding().
116379 */
116380 static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){
116381 assert( iDb>=0 && iDb<pToplevel->db->nDb );
116382 assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 );
116383 assert( iDb<SQLITE_MAX_DB );
116384 assert( sqlite3SchemaMutexHeld(pToplevel->db, iDb, 0) );
116385 if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
116386 DbMaskSet(pToplevel->cookieMask, iDb);
116387 if( !OMIT_TEMPDB && iDb==1 ){
116388 sqlite3OpenTempDatabase(pToplevel);
@@ -117505,14 +117582,16 @@
117582 }
117583
117584 /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
117585 ** and the SELECT subtree. */
117586 pSrc->a[0].pTab = 0;
117587 pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);
117588 pSrc->a[0].pTab = pTab;
117589 if( pSrc->a[0].fg.isIndexedBy ){
117590 pSrc->a[0].u2.pIBIndex = 0;
117591 pSrc->a[0].fg.isIndexedBy = 0;
117592 sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy);
117593 }else if( pSrc->a[0].fg.isCte ){
117594 pSrc->a[0].u2.pCteUse->nUse++;
117595 }
117596
117597 /* generate the SELECT expression tree. */
@@ -129355,11 +129434,11 @@
129434 ** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate
129435 **
129436 ** Checkpoint the database.
129437 */
129438 case PragTyp_WAL_CHECKPOINT: {
129439 int iBt = (pId2->z?iDb:SQLITE_MAX_DB);
129440 int eMode = SQLITE_CHECKPOINT_PASSIVE;
129441 if( zRight ){
129442 if( sqlite3StrICmp(zRight, "full")==0 ){
129443 eMode = SQLITE_CHECKPOINT_FULL;
129444 }else if( sqlite3StrICmp(zRight, "restart")==0 ){
@@ -130571,17 +130650,25 @@
130650 ** for common cleanups that happen on most calls. But for less
130651 ** common cleanups, we save a single NULL-pointer comparison in
130652 ** sqlite3ParserReset(), which reduces the total CPU cycle count.
130653 **
130654 ** If a memory allocation error occurs, then the cleanup happens immediately.
130655 ** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
130656 ** pParse->earlyCleanup flag is set in that case. Calling code show verify
130657 ** that test cases exist for which this happens, to guard against possible
130658 ** use-after-free errors following an OOM. The preferred way to do this is
130659 ** to immediately follow the call to this routine with:
130660 **
130661 ** testcase( pParse->earlyCleanup );
130662 **
130663 ** This routine returns a copy of its pPtr input (the third parameter)
130664 ** except if an early cleanup occurs, in which case it returns NULL. So
130665 ** another way to check for early cleanup is to check the return value.
130666 ** Or, stop using the pPtr parameter with this call and use only its
130667 ** return value thereafter. Something like this:
130668 **
130669 ** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj);
130670 */
130671 SQLITE_PRIVATE void *sqlite3ParserAddCleanup(
130672 Parse *pParse, /* Destroy when this Parser finishes */
130673 void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
130674 void *pPtr /* Pointer to object to be cleaned up */
@@ -131075,16 +131162,20 @@
131162 sqlite3ExprDelete(db, p->pWhere);
131163 sqlite3ExprListDelete(db, p->pGroupBy);
131164 sqlite3ExprDelete(db, p->pHaving);
131165 sqlite3ExprListDelete(db, p->pOrderBy);
131166 sqlite3ExprDelete(db, p->pLimit);
131167 if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
131168 #ifndef SQLITE_OMIT_WINDOWFUNC
131169 if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
131170 sqlite3WindowListDelete(db, p->pWinDefn);
131171 }
131172 while( p->pWin ){
131173 assert( p->pWin->ppThis==&p->pWin );
131174 sqlite3WindowUnlinkFromSelect(p->pWin);
131175 }
131176 #endif
 
131177 if( bFree ) sqlite3DbFreeNN(db, p);
131178 p = pPrior;
131179 bFree = 1;
131180 }
131181 }
@@ -131396,10 +131487,13 @@
131487 static void unsetJoinExpr(Expr *p, int iTable){
131488 while( p ){
131489 if( ExprHasProperty(p, EP_FromJoin)
131490 && (iTable<0 || p->iRightJoinTable==iTable) ){
131491 ExprClearProperty(p, EP_FromJoin);
131492 }
131493 if( p->op==TK_COLUMN && p->iTable==iTable ){
131494 ExprClearProperty(p, EP_CanBeNull);
131495 }
131496 if( p->op==TK_FUNCTION && p->x.pList ){
131497 int i;
131498 for(i=0; i<p->x.pList->nExpr; i++){
131499 unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
@@ -132373,11 +132467,11 @@
132467 }
132468
132469 /*
132470 ** Name of the connection operator, used for error messages.
132471 */
132472 SQLITE_PRIVATE const char *sqlite3SelectOpName(int id){
132473 char *z;
132474 switch( id ){
132475 case TK_ALL: z = "UNION ALL"; break;
132476 case TK_INTERSECT: z = "INTERSECT"; break;
132477 case TK_EXCEPT: z = "EXCEPT"; break;
@@ -133592,16 +133686,12 @@
133686 assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
133687 assert( p->selFlags & SF_Compound );
133688 db = pParse->db;
133689 pPrior = p->pPrior;
133690 dest = *pDest;
133691 assert( pPrior->pOrderBy==0 );
133692 assert( pPrior->pLimit==0 );
 
 
 
 
133693
133694 v = sqlite3GetVdbe(pParse);
133695 assert( v!=0 ); /* The VDBE already created by calling function */
133696
133697 /* Create the destination temporary table if necessary
@@ -133654,11 +133744,11 @@
133744 assert( !pPrior->pLimit );
133745 pPrior->iLimit = p->iLimit;
133746 pPrior->iOffset = p->iOffset;
133747 pPrior->pLimit = p->pLimit;
133748 rc = sqlite3Select(pParse, pPrior, &dest);
133749 pPrior->pLimit = 0;
133750 if( rc ){
133751 goto multi_select_end;
133752 }
133753 p->pPrior = 0;
133754 p->iLimit = pPrior->iLimit;
@@ -133675,12 +133765,12 @@
133765 rc = sqlite3Select(pParse, p, &dest);
133766 testcase( rc!=SQLITE_OK );
133767 pDelete = p->pPrior;
133768 p->pPrior = pPrior;
133769 p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
133770 if( p->pLimit
133771 && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit)
133772 && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
133773 ){
133774 p->nSelectRow = sqlite3LogEst((u64)nLimit);
133775 }
133776 if( addr ){
@@ -133740,11 +133830,11 @@
133830 p->pPrior = 0;
133831 pLimit = p->pLimit;
133832 p->pLimit = 0;
133833 uniondest.eDest = op;
133834 ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
133835 sqlite3SelectOpName(p->op)));
133836 rc = sqlite3Select(pParse, p, &uniondest);
133837 testcase( rc!=SQLITE_OK );
133838 assert( p->pOrderBy==0 );
133839 pDelete = p->pPrior;
133840 p->pPrior = pPrior;
@@ -133816,11 +133906,11 @@
133906 p->pPrior = 0;
133907 pLimit = p->pLimit;
133908 p->pLimit = 0;
133909 intersectdest.iSDParm = tab2;
133910 ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
133911 sqlite3SelectOpName(p->op)));
133912 rc = sqlite3Select(pParse, p, &intersectdest);
133913 testcase( rc!=SQLITE_OK );
133914 pDelete = p->pPrior;
133915 p->pPrior = pPrior;
133916 if( p->nSelectRow>pPrior->nSelectRow ){
@@ -133925,11 +134015,12 @@
134015 SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
134016 if( p->selFlags & SF_Values ){
134017 sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
134018 }else{
134019 sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
134020 " do not have the same number of result columns",
134021 sqlite3SelectOpName(p->op));
134022 }
134023 }
134024
134025 /*
134026 ** Code an output subroutine for a coroutine implementation of a
@@ -134022,14 +134113,12 @@
134113 ** store the results in the appropriate memory cell and break out
134114 ** of the scan loop. Note that the select might return multiple columns
134115 ** if it is the RHS of a row-value IN operator.
134116 */
134117 case SRT_Mem: {
134118 testcase( pIn->nSdst>1 );
134119 sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
 
 
134120 /* The LIMIT clause will jump out of the loop for us */
134121 break;
134122 }
134123 #endif /* #ifndef SQLITE_OMIT_SUBQUERY */
134124
@@ -134317,11 +134406,11 @@
134406 regOutA = ++pParse->nMem;
134407 regOutB = ++pParse->nMem;
134408 sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
134409 sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
134410
134411 ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlite3SelectOpName(p->op)));
134412
134413 /* Generate a coroutine to evaluate the SELECT statement to the
134414 ** left of the compound operator - the "A" select.
134415 */
134416 addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
@@ -135479,10 +135568,39 @@
135568 }
135569 }while( x.nChng );
135570 return nChng;
135571 }
135572
135573 #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
135574 # if !defined(SQLITE_OMIT_WINDOWFUNC)
135575 /*
135576 ** This function is called to determine whether or not it is safe to
135577 ** push WHERE clause expression pExpr down to FROM clause sub-query
135578 ** pSubq, which contains at least one window function. Return 1
135579 ** if it is safe and the expression should be pushed down, or 0
135580 ** otherwise.
135581 **
135582 ** It is only safe to push the expression down if it consists only
135583 ** of constants and copies of expressions that appear in the PARTITION
135584 ** BY clause of all window function used by the sub-query. It is safe
135585 ** to filter out entire partitions, but not rows within partitions, as
135586 ** this may change the results of the window functions.
135587 **
135588 ** At the time this function is called it is guaranteed that
135589 **
135590 ** * the sub-query uses only one distinct window frame, and
135591 ** * that the window frame has a PARTITION BY clase.
135592 */
135593 static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
135594 assert( pSubq->pWin->pPartition );
135595 assert( (pSubq->selFlags & SF_MultiPart)==0 );
135596 assert( pSubq->pPrior==0 );
135597 return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition);
135598 }
135599 # endif /* SQLITE_OMIT_WINDOWFUNC */
135600 #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
135601
135602 #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
135603 /*
135604 ** Make copies of relevant WHERE clause terms of the outer query into
135605 ** the WHERE clause of subquery. Example:
135606 **
@@ -135526,13 +135644,24 @@
135644 **
135645 ** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9).
135646 ** But if the (b2=2) term were to be pushed down into the bb subquery,
135647 ** then the (1,1,NULL) row would be suppressed.
135648 **
135649 ** (6) Window functions make things tricky as changes to the WHERE clause
135650 ** of the inner query could change the window over which window
135651 ** functions are calculated. Therefore, do not attempt the optimization
135652 ** if:
135653 **
135654 ** (6a) The inner query uses multiple incompatible window partitions.
135655 **
135656 ** (6b) The inner query is a compound and uses window-functions.
135657 **
135658 ** (6c) The WHERE clause does not consist entirely of constants and
135659 ** copies of expressions found in the PARTITION BY clause of
135660 ** all window-functions used by the sub-query. It is safe to
135661 ** filter out entire partitions, as this does not change the
135662 ** window over which any window-function is calculated.
135663 **
135664 ** (7) The inner query is a Common Table Expression (CTE) that should
135665 ** be materialized. (This restriction is implemented in the calling
135666 ** routine.)
135667 **
@@ -135546,17 +135675,21 @@
135675 int iCursor, /* Cursor number of the subquery */
135676 int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
135677 ){
135678 Expr *pNew;
135679 int nChng = 0;
 
135680 if( pWhere==0 ) return 0;
135681 if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
135682
135683 #ifndef SQLITE_OMIT_WINDOWFUNC
135684 if( pSubq->pPrior ){
135685 Select *pSel;
135686 for(pSel=pSubq; pSel; pSel=pSel->pPrior){
135687 if( pSel->pWin ) return 0; /* restriction (6b) */
135688 }
135689 }else{
135690 if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
135691 }
135692 #endif
135693
135694 #ifdef SQLITE_DEBUG
135695 /* Only the first term of a compound can have a WITH clause. But make
@@ -135599,10 +135732,18 @@
135732 x.iTable = iCursor;
135733 x.iNewTable = iCursor;
135734 x.isLeftJoin = 0;
135735 x.pEList = pSubq->pEList;
135736 pNew = substExpr(&x, pNew);
135737 #ifndef SQLITE_OMIT_WINDOWFUNC
135738 if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
135739 /* Restriction 6c has prevented push-down in this case */
135740 sqlite3ExprDelete(pParse->db, pNew);
135741 nChng--;
135742 break;
135743 }
135744 #endif
135745 if( pSubq->selFlags & SF_Aggregate ){
135746 pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
135747 }else{
135748 pSubq->pWhere = sqlite3ExprAnd(pParse, pSubq->pWhere, pNew);
135749 }
@@ -136190,11 +136331,14 @@
136331 if( IsVirtual(pTab) || pTab->pSelect ){
136332 i16 nCol;
136333 u8 eCodeOrig = pWalker->eCode;
136334 if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
136335 assert( pFrom->pSelect==0 );
136336 if( pTab->pSelect
136337 && (db->flags & SQLITE_EnableView)==0
136338 && pTab->pSchema!=db->aDb[1].pSchema
136339 ){
136340 sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
136341 pTab->zName);
136342 }
136343 #ifndef SQLITE_OMIT_VIRTUALTABLE
136344 if( IsVirtual(pTab)
@@ -136969,12 +137113,23 @@
137113 if( IgnorableDistinct(pDest) ){
137114 assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
137115 pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
137116 pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo );
137117 /* All of these destinations are also able to ignore the ORDER BY clause */
137118 if( p->pOrderBy ){
137119 #if SELECTTRACE_ENABLED
137120 SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
137121 if( sqlite3SelectTrace & 0x100 ){
137122 sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
137123 }
137124 #endif
137125 sqlite3ParserAddCleanup(pParse,
137126 (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
137127 p->pOrderBy);
137128 testcase( pParse->earlyCleanup );
137129 p->pOrderBy = 0;
137130 }
137131 p->selFlags &= ~SF_Distinct;
137132 p->selFlags |= SF_NoopOrderBy;
137133 }
137134 sqlite3SelectPrep(pParse, p, 0);
137135 if( pParse->nErr || db->mallocFailed ){
@@ -137589,10 +137744,11 @@
137744 */
137745 pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
137746 if( pAggInfo ){
137747 sqlite3ParserAddCleanup(pParse,
137748 (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
137749 testcase( pParse->earlyCleanup );
137750 }
137751 if( db->mallocFailed ){
137752 goto select_end;
137753 }
137754 pAggInfo->selId = p->selId;
@@ -147345,11 +147501,14 @@
147501 sqlite3 *db = pParse->db;
147502
147503 assert( pExpr->op==TK_EXISTS );
147504 assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
147505
147506 if( pSel->selFlags & SF_Aggregate ) return;
147507 #ifndef SQLITE_OMIT_WINDOWFUNC
147508 if( pSel->pWin ) return;
147509 #endif
147510 if( pSel->pPrior ) return;
147511 if( pSel->pWhere==0 ) return;
147512 if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;
147513
147514 pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -147542,10 +147701,16 @@
147701 pNew->u.x.leftColumn = aiCurCol[1];
147702 testcase( (prereqLeft | extraRight) != prereqLeft );
147703 pNew->prereqRight = prereqLeft | extraRight;
147704 pNew->prereqAll = prereqAll;
147705 pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
147706 }else if( op==TK_ISNULL && 0==sqlite3ExprCanBeNull(pLeft) ){
147707 pExpr->op = TK_TRUEFALSE;
147708 pExpr->u.zToken = "false";
147709 ExprSetProperty(pExpr, EP_IsFalse);
147710 pTerm->prereqAll = 0;
147711 pTerm->eOperator = 0;
147712 }
147713 }
147714
147715 #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
147716 /* If a term is the BETWEEN operator, create two new virtual terms
@@ -153019,11 +153184,11 @@
153184 }
153185 }
153186 if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
153187 pWInfo->revMask = ALLBITS;
153188 }
153189 if( pParse->nErr || db->mallocFailed ){
153190 goto whereBeginError;
153191 }
153192 #ifdef WHERETRACE_ENABLED
153193 if( sqlite3WhereTrace ){
153194 sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
@@ -154961,19 +155126,23 @@
155126 ** to be processed as part of SELECT statement pSel). The window is linked
155127 ** in if either (a) there are no other windows already linked to this
155128 ** SELECT, or (b) the windows already linked use a compatible window frame.
155129 */
155130 SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){
155131 if( pSel ){
155132 if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){
155133 pWin->pNextWin = pSel->pWin;
155134 if( pSel->pWin ){
155135 pSel->pWin->ppThis = &pWin->pNextWin;
155136 }
155137 pSel->pWin = pWin;
155138 pWin->ppThis = &pSel->pWin;
155139 }else{
155140 if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){
155141 pSel->selFlags |= SF_MultiPart;
155142 }
155143 }
155144 }
155145 }
155146
155147 /*
155148 ** Return 0 if the two window objects are identical, 1 if they are
@@ -155718,10 +155887,11 @@
155887 int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */
155888 int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */
155889 int regString = ++pParse->nMem; /* Reg. for constant value '' */
155890 int arith = OP_Add; /* OP_Add or OP_Subtract */
155891 int addrGe; /* Jump destination */
155892 CollSeq *pColl;
155893
155894 assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
155895 assert( pOrderBy && pOrderBy->nExpr==1 );
155896 if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
155897 switch( op ){
@@ -155808,10 +155978,12 @@
155978
155979 /* Compare registers reg2 and reg1, taking the jump if required. Note that
155980 ** control skips over this test if the BIGNULL flag is set and either
155981 ** reg1 or reg2 contain a NULL value. */
155982 sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
155983 pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr);
155984 sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ);
155985 sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
155986
155987 assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
155988 testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
155989 testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
@@ -156814,15 +156986,25 @@
156986 ** SQLITE_LIMIT_COMPOUND_SELECT.
156987 */
156988 static void parserDoubleLinkSelect(Parse *pParse, Select *p){
156989 assert( p!=0 );
156990 if( p->pPrior ){
156991 Select *pNext = 0, *pLoop = p;
156992 int mxSelect, cnt = 1;
156993 while(1){
156994 pLoop->pNext = pNext;
156995 pLoop->selFlags |= SF_Compound;
156996 pNext = pLoop;
156997 pLoop = pLoop->pPrior;
156998 if( pLoop==0 ) break;
156999 cnt++;
157000 if( pLoop->pOrderBy || pLoop->pLimit ){
157001 sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
157002 pLoop->pOrderBy!=0 ? "ORDER BY" : "LIMIT",
157003 sqlite3SelectOpName(pNext->op));
157004 break;
157005 }
157006 }
157007 if( (p->selFlags & SF_MultiValue)==0 &&
157008 (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 &&
157009 cnt>mxSelect
157010 ){
@@ -166129,11 +166311,11 @@
166311 ){
166312 #ifdef SQLITE_OMIT_WAL
166313 return SQLITE_OK;
166314 #else
166315 int rc; /* Return code */
166316 int iDb; /* Schema to checkpoint */
166317
166318 #ifdef SQLITE_ENABLE_API_ARMOR
166319 if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
166320 #endif
166321
@@ -166152,10 +166334,12 @@
166334 }
166335
166336 sqlite3_mutex_enter(db->mutex);
166337 if( zDb && zDb[0] ){
166338 iDb = sqlite3FindDbName(db, zDb);
166339 }else{
166340 iDb = SQLITE_MAX_DB; /* This means process all schemas */
166341 }
166342 if( iDb<0 ){
166343 rc = SQLITE_ERROR;
166344 sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
166345 }else{
@@ -166200,11 +166384,11 @@
166384 **
166385 ** The mutex on database handle db should be held by the caller. The mutex
166386 ** associated with the specific b-tree being checkpointed is taken by
166387 ** this function while the checkpoint is running.
166388 **
166389 ** If iDb is passed SQLITE_MAX_DB then all attached databases are
166390 ** checkpointed. If an error is encountered it is returned immediately -
166391 ** no attempt is made to checkpoint any remaining databases.
166392 **
166393 ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART
166394 ** or TRUNCATE.
@@ -166215,13 +166399,15 @@
166399 int bBusy = 0; /* True if SQLITE_BUSY has been encountered */
166400
166401 assert( sqlite3_mutex_held(db->mutex) );
166402 assert( !pnLog || *pnLog==-1 );
166403 assert( !pnCkpt || *pnCkpt==-1 );
166404 testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */
166405 testcase( iDb==SQLITE_MAX_DB );
166406
166407 for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
166408 if( i==iDb || iDb==SQLITE_MAX_DB ){
166409 rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
166410 pnLog = 0;
166411 pnCkpt = 0;
166412 if( rc==SQLITE_BUSY ){
166413 bBusy = 1;
@@ -174914,13 +175100,13 @@
175100 res = fts3PoslistNearMerge(
175101 &pOut, aTmp, nParam1, nParam2, paPoslist, &p2
175102 );
175103 if( res ){
175104 nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
175105 assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 );
175106 if( nNew>=0 && nNew<=pPhrase->doclist.nList ){
175107 assert( pPhrase->doclist.pList[nNew]=='\0' );
 
175108 memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
175109 pPhrase->doclist.nList = nNew;
175110 }
175111 *paPoslist = pPhrase->doclist.pList;
175112 *pnToken = pPhrase->nToken;
@@ -176850,10 +177036,15 @@
177036
177037 if( sqlite3_fts3_enable_parentheses ){
177038 if( *zInput=='(' ){
177039 int nConsumed = 0;
177040 pParse->nNest++;
177041 #if !defined(SQLITE_MAX_EXPR_DEPTH)
177042 if( pParse->nNest>1000 ) return SQLITE_ERROR;
177043 #elif SQLITE_MAX_EXPR_DEPTH>0
177044 if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR;
177045 #endif
177046 rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
177047 *pnConsumed = (int)(zInput - z) + 1 + nConsumed;
177048 return rc;
177049 }else if( *zInput==')' ){
177050 pParse->nNest--;
@@ -184253,31 +184444,30 @@
184444 if( pNode->block.a){
184445 rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
184446 while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
184447 blobGrowBuffer(&pNode->key, reader.term.n, &rc);
184448 if( rc==SQLITE_OK ){
184449 assert_fts3_nc( reader.term.n>0 || reader.aNode==0 );
184450 if( reader.term.n>0 ){
 
184451 memcpy(pNode->key.a, reader.term.a, reader.term.n);
184452 }
184453 pNode->key.n = reader.term.n;
184454 if( i>0 ){
184455 char *aBlock = 0;
184456 int nBlock = 0;
184457 pNode = &pWriter->aNodeWriter[i-1];
184458 pNode->iBlock = reader.iChild;
184459 rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
184460 blobGrowBuffer(&pNode->block,
184461 MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
184462 );
184463 if( rc==SQLITE_OK ){
184464 memcpy(pNode->block.a, aBlock, nBlock);
184465 pNode->block.n = nBlock;
184466 memset(&pNode->block.a[nBlock], 0, FTS3_NODE_PADDING);
184467 }
184468 sqlite3_free(aBlock);
184469 }
184470 }
184471 }
184472 nodeReaderRelease(&reader);
184473 }
@@ -187755,10 +187945,11 @@
187945 memset(pCsr, 0, sizeof(unicode_cursor));
187946
187947 pCsr->aInput = (const unsigned char *)aInput;
187948 if( aInput==0 ){
187949 pCsr->nInput = 0;
187950 pCsr->aInput = (const unsigned char*)"";
187951 }else if( nInput<0 ){
187952 pCsr->nInput = (int)strlen(aInput);
187953 }else{
187954 pCsr->nInput = nInput;
187955 }
@@ -224323,21 +224514,26 @@
224514 pIter->nPoslist = ((int)(p[0])) >> 1;
224515 pIter->nSize = 1;
224516 }
224517
224518 pIter->aPoslist = p;
224519 if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){
224520 pIter->aPoslist = 0;
224521 }
224522 }
224523 }
224524
224525 static void fts5DoclistIterInit(
224526 Fts5Buffer *pBuf,
224527 Fts5DoclistIter *pIter
224528 ){
224529 memset(pIter, 0, sizeof(*pIter));
224530 if( pBuf->n>0 ){
224531 pIter->aPoslist = pBuf->p;
224532 pIter->aEof = &pBuf->p[pBuf->n];
224533 fts5DoclistIterNext(pIter);
224534 }
224535 }
224536
224537 #if 0
224538 /*
224539 ** Append a doclist to buffer pBuf.
@@ -225095,12 +225291,13 @@
225291 ** Return the current term.
225292 */
225293 static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
225294 int n;
225295 const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
225296 assert_nc( z || n<=1 );
225297 *pn = n-1;
225298 return (z ? &z[1] : 0);
225299 }
225300
225301 /*
225302 ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
225303 */
@@ -228382,11 +228579,12 @@
228579 ){
228580 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
228581 int n;
228582 int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
228583 if( rc==SQLITE_OK ){
228584 assert( pIter->a || n==0 );
228585 pIter->b = (pIter->a ? &pIter->a[n] : 0);
228586 *piCol = 0;
228587 *piOff = 0;
228588 fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
228589 }
228590 return rc;
@@ -228441,19 +228639,21 @@
228639 pIter->a = &pSorter->aPoslist[i1];
228640 }else{
228641 rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
228642 }
228643 if( rc==SQLITE_OK ){
228644 assert( pIter->a || n==0 );
228645 pIter->b = (pIter->a ? &pIter->a[n] : 0);
228646 *piCol = 0;
228647 fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
228648 }
228649 }else{
228650 int n;
228651 rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
228652 if( rc==SQLITE_OK ){
228653 assert( pIter->a || n==0 );
228654 pIter->b = (pIter->a ? &pIter->a[n] : 0);
228655 if( n<=0 ){
228656 *piCol = -1;
228657 }else if( pIter->a[0]==0x01 ){
228658 pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
228659 }else{
@@ -228927,11 +229127,11 @@
229127 assert( nArg>0 );
229128 rc = SQLITE_ERROR;
229129 *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
229130 }else{
229131 rc = pMod->x.xCreate(
229132 pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
229133 );
229134 pConfig->pTokApi = &pMod->x;
229135 if( rc!=SQLITE_OK ){
229136 if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
229137 }else{
@@ -228990,11 +229190,11 @@
229190 int nArg, /* Number of args */
229191 sqlite3_value **apUnused /* Function arguments */
229192 ){
229193 assert( nArg==0 );
229194 UNUSED_PARAM2(nArg, apUnused);
229195 sqlite3_result_text(pCtx, "fts5: 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b", -1, SQLITE_TRANSIENT);
229196 }
229197
229198 /*
229199 ** Return true if zName is the extension on one of the shadow tables used
229200 ** by this module.
@@ -233916,12 +234116,12 @@
234116 }
234117 #endif /* SQLITE_CORE */
234118 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
234119
234120 /************** End of stmt.c ************************************************/
234121 #if __LINE__!=234121
234122 #undef SQLITE_SOURCE_ID
234123 #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115alt2"
234124 #endif
234125 /* Return the source-id for this library */
234126 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
234127 /************************** End of sqlite3.c ******************************/
234128
+23 -12
--- 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.35.0"
127127
#define SQLITE_VERSION_NUMBER 3035000
128
-#define SQLITE_SOURCE_ID "2021-02-22 16:42:09 b5a0778cc5a98a864bea72670f83262da940aceb91fa4cdf46ec097337a3alt1"
128
+#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
129129
130130
/*
131131
** CAPI3REF: Run-Time Library Version Numbers
132132
** KEYWORDS: sqlite3_version sqlite3_sourceid
133133
**
@@ -2130,11 +2130,17 @@
21302130
** The first argument is an integer which is 0 to disable views,
21312131
** positive to enable views or negative to leave the setting unchanged.
21322132
** The second parameter is a pointer to an integer into which
21332133
** is written 0 or 1 to indicate whether views are disabled or enabled
21342134
** following this call. The second parameter may be a NULL pointer, in
2135
-** which case the view setting is not reported back. </dd>
2135
+** which case the view setting is not reported back.
2136
+**
2137
+** <p>Originally this option disabled all views. ^(However, since
2138
+** SQLite version 3.35.0, TEMP views are still allowed even if
2139
+** this option is off. So, in other words, this option now only disables
2140
+** views in the main database schema or in the schemas of ATTACH-ed
2141
+** databases.)^ </dd>
21362142
**
21372143
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
21382144
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
21392145
** <dd> ^This option is used to enable or disable the
21402146
** [fts3_tokenizer()] function which is part of the
@@ -10554,22 +10560,27 @@
1055410560
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
1055510561
** created by [sqlite3changeset_start()]. In the latter case, the most recent
1055610562
** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
1055710563
** is not the case, this function returns [SQLITE_MISUSE].
1055810564
**
10559
-** If argument pzTab is not NULL, then *pzTab is set to point to a
10560
-** nul-terminated utf-8 encoded string containing the name of the table
10561
-** affected by the current change. The buffer remains valid until either
10562
-** sqlite3changeset_next() is called on the iterator or until the
10563
-** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
10564
-** set to the number of columns in the table affected by the change. If
10565
-** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
10565
+** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
10566
+** outputs are set through these pointers:
10567
+**
10568
+** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
10569
+** depending on the type of change that the iterator currently points to;
10570
+**
10571
+** *pnCol is set to the number of columns in the table affected by the change; and
10572
+**
10573
+** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
10574
+** the name of the table affected by the current change. The buffer remains
10575
+** valid until either sqlite3changeset_next() is called on the iterator
10576
+** or until the conflict-handler function returns.
10577
+**
10578
+** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
1056610579
** is an indirect change, or false (0) otherwise. See the documentation for
1056710580
** [sqlite3session_indirect()] for a description of direct and indirect
10568
-** changes. Finally, if pOp is not NULL, then *pOp is set to one of
10569
-** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
10570
-** type of change that the iterator currently points to.
10581
+** changes.
1057110582
**
1057210583
** If no error occurs, SQLITE_OK is returned. If an error does occur, an
1057310584
** SQLite error code is returned. The values of the output variables may not
1057410585
** be trusted in this case.
1057510586
*/
1057610587
--- 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.35.0"
127 #define SQLITE_VERSION_NUMBER 3035000
128 #define SQLITE_SOURCE_ID "2021-02-22 16:42:09 b5a0778cc5a98a864bea72670f83262da940aceb91fa4cdf46ec097337a3alt1"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
@@ -2130,11 +2130,17 @@
2130 ** The first argument is an integer which is 0 to disable views,
2131 ** positive to enable views or negative to leave the setting unchanged.
2132 ** The second parameter is a pointer to an integer into which
2133 ** is written 0 or 1 to indicate whether views are disabled or enabled
2134 ** following this call. The second parameter may be a NULL pointer, in
2135 ** which case the view setting is not reported back. </dd>
 
 
 
 
 
 
2136 **
2137 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
2138 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2139 ** <dd> ^This option is used to enable or disable the
2140 ** [fts3_tokenizer()] function which is part of the
@@ -10554,22 +10560,27 @@
10554 ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
10555 ** created by [sqlite3changeset_start()]. In the latter case, the most recent
10556 ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
10557 ** is not the case, this function returns [SQLITE_MISUSE].
10558 **
10559 ** If argument pzTab is not NULL, then *pzTab is set to point to a
10560 ** nul-terminated utf-8 encoded string containing the name of the table
10561 ** affected by the current change. The buffer remains valid until either
10562 ** sqlite3changeset_next() is called on the iterator or until the
10563 ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
10564 ** set to the number of columns in the table affected by the change. If
10565 ** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
 
 
 
 
 
 
 
10566 ** is an indirect change, or false (0) otherwise. See the documentation for
10567 ** [sqlite3session_indirect()] for a description of direct and indirect
10568 ** changes. Finally, if pOp is not NULL, then *pOp is set to one of
10569 ** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
10570 ** type of change that the iterator currently points to.
10571 **
10572 ** If no error occurs, SQLITE_OK is returned. If an error does occur, an
10573 ** SQLite error code is returned. The values of the output variables may not
10574 ** be trusted in this case.
10575 */
10576
--- 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.35.0"
127 #define SQLITE_VERSION_NUMBER 3035000
128 #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
@@ -2130,11 +2130,17 @@
2130 ** The first argument is an integer which is 0 to disable views,
2131 ** positive to enable views or negative to leave the setting unchanged.
2132 ** The second parameter is a pointer to an integer into which
2133 ** is written 0 or 1 to indicate whether views are disabled or enabled
2134 ** following this call. The second parameter may be a NULL pointer, in
2135 ** which case the view setting is not reported back.
2136 **
2137 ** <p>Originally this option disabled all views. ^(However, since
2138 ** SQLite version 3.35.0, TEMP views are still allowed even if
2139 ** this option is off. So, in other words, this option now only disables
2140 ** views in the main database schema or in the schemas of ATTACH-ed
2141 ** databases.)^ </dd>
2142 **
2143 ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
2144 ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
2145 ** <dd> ^This option is used to enable or disable the
2146 ** [fts3_tokenizer()] function which is part of the
@@ -10554,22 +10560,27 @@
10560 ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
10561 ** created by [sqlite3changeset_start()]. In the latter case, the most recent
10562 ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
10563 ** is not the case, this function returns [SQLITE_MISUSE].
10564 **
10565 ** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
10566 ** outputs are set through these pointers:
10567 **
10568 ** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
10569 ** depending on the type of change that the iterator currently points to;
10570 **
10571 ** *pnCol is set to the number of columns in the table affected by the change; and
10572 **
10573 ** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
10574 ** the name of the table affected by the current change. The buffer remains
10575 ** valid until either sqlite3changeset_next() is called on the iterator
10576 ** or until the conflict-handler function returns.
10577 **
10578 ** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
10579 ** is an indirect change, or false (0) otherwise. See the documentation for
10580 ** [sqlite3session_indirect()] for a description of direct and indirect
10581 ** changes.
 
 
10582 **
10583 ** If no error occurs, SQLITE_OK is returned. If an error does occur, an
10584 ** SQLite error code is returned. The values of the output variables may not
10585 ** be trusted in this case.
10586 */
10587
+71 -37
--- src/style.c
+++ src/style.c
@@ -366,51 +366,79 @@
366366
va_end(ap);
367367
}
368368
}
369369
370370
/*
371
-** Create a TH1 variable containing the URL for the specified config
372
-** resource. The resulting variable name will be of the form
373
-** $[zVarPrefix]_url.
371
+** Create a TH1 variable containing the URL for the stylesheet.
372
+**
373
+** The name of the new variable will be "stylesheet_url".
374
+**
375
+** The value will be a URL for accessing the appropriate stylesheet.
376
+** This URL will include query parameters such as "id=" and "once&skin="
377
+** to cause the correct stylesheet to be loaded after a skin change
378
+** or after a change to the stylesheet.
374379
*/
375
-static void url_var(
376
- const char *zVarPrefix,
377
- const char *zConfigName,
378
- const char *zPageName
379
-){
380
- char *zVarName = mprintf("%s_url", zVarPrefix);
381
- char *zUrl = 0; /* stylesheet URL */
382
- int hasBuiltin = 0; /* true for built-in page-specific CSS */
383
-
384
- if(0==strcmp("css",zConfigName)){
385
- /* Account for page-specific CSS, appending a /{{g.zPath}} to the
386
- ** url only if we have a corresponding built-in page-specific CSS
387
- ** file. Do not append it to all pages because we would
388
- ** effectively cache-bust all pages which do not have
389
- ** page-specific CSS. */
390
- char * zBuiltin = mprintf("style.%s.css", g.zPath);
391
- hasBuiltin = builtin_file(zBuiltin,0)!=0;
392
- fossil_free(zBuiltin);
393
- }
394
- zUrl = mprintf("%R/%s%s%s?id=%x", zPageName,
395
- hasBuiltin ? "/" : "", hasBuiltin ? g.zPath : "",
396
- skin_id(zConfigName));
397
- Th_Store(zVarName, zUrl);
398
- fossil_free(zUrl);
399
- fossil_free(zVarName);
380
+static void stylesheet_url_var(void){
381
+ char *zBuiltin; /* Auxiliary page-specific CSS page */
382
+ Blob url; /* The URL */
383
+
384
+ /* Initialize the URL to its baseline */
385
+ url = empty_blob;
386
+ blob_appendf(&url, "%R/style.css");
387
+
388
+ /* If page-specific CSS exists for the current page, then append
389
+ ** the pathname for the page-specific CSS. The default CSS is
390
+ **
391
+ ** /style.css
392
+ **
393
+ ** But for the "/wikiedit" page (to name but one example), we
394
+ ** append a path as follows:
395
+ **
396
+ ** /style.css/wikiedit
397
+ **
398
+ ** The /style.css page (implemented below) will detect this extra "wikiedit"
399
+ ** path information and include the page-specific CSS along with the
400
+ ** default CSS when it delivers the page.
401
+ */
402
+ zBuiltin = mprintf("style.%s.css", g.zPath);
403
+ if( builtin_file(zBuiltin,0)!=0 ){
404
+ blob_appendf(&url, "/%s", g.zPath);
405
+ }
406
+ fossil_free(zBuiltin);
407
+
408
+ /* Add query parameters that will change whenever the skin changes
409
+ ** or after any updates to the CSS files
410
+ */
411
+ blob_appendf(&url, "?id=%x", skin_id("css"));
412
+ if( P("once")!=0 && P("skin")!=0 ){
413
+ blob_appendf(&url, "&skin=%s&once", skin_in_use());
414
+ }
415
+
416
+ /* Generate the CSS URL variable */
417
+ Th_Store("stylesheet_url", blob_str(&url));
418
+ blob_reset(&url);
400419
}
401420
402421
/*
403
-** Create a TH1 variable containing the URL for the specified config image.
422
+** Create a TH1 variable containing the URL for the specified image.
404423
** The resulting variable name will be of the form $[zImageName]_image_url.
424
+** The value will be a URL that includes an id= query parameter that
425
+** changes if the underlying resource changes or if a different skin
426
+** is selected.
405427
*/
406428
static void image_url_var(const char *zImageName){
407
- char *zVarPrefix = mprintf("%s_image", zImageName);
408
- char *zConfigName = mprintf("%s-image", zImageName);
409
- url_var(zVarPrefix, zConfigName, zImageName);
410
- free(zVarPrefix);
411
- free(zConfigName);
429
+ char *zVarName; /* Name of the new TH1 variable */
430
+ char *zResource; /* Name of CONFIG entry holding content */
431
+ char *zUrl; /* The URL */
432
+
433
+ zResource = mprintf("%s-image", zImageName);
434
+ zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource));
435
+ free(zResource);
436
+ zVarName = mprintf("%s_image_url", zImageName);
437
+ Th_Store(zVarName, zUrl);
438
+ free(zVarName);
439
+ free(zUrl);
412440
}
413441
414442
/*
415443
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
416444
** Javascript module, and generates HTML elements with the following IDs:
@@ -519,10 +547,11 @@
519547
** default is used instead:
520548
**
521549
** default-src 'self' data:;
522550
** script-src 'self' 'nonce-$nonce';
523551
** style-src 'self' 'unsafe-inline';
552
+** img-src * data:;
524553
**
525554
** The text '$nonce' is replaced by style_nonce() if and whereever it
526555
** occurs in the input string.
527556
**
528557
** The string returned is obtained from fossil_malloc() and
@@ -530,11 +559,12 @@
530559
*/
531560
char *style_csp(int toHeader){
532561
static const char zBackupCSP[] =
533562
"default-src 'self' data:; "
534563
"script-src 'self' 'nonce-$nonce'; "
535
- "style-src 'self' 'unsafe-inline'";
564
+ "style-src 'self' 'unsafe-inline'; "
565
+ "img-src * data:";
536566
const char *zFormat;
537567
Blob csp;
538568
char *zNonce;
539569
char *zCsp;
540570
int i;
@@ -609,11 +639,11 @@
609639
@ Tags /taglist o wideonly
610640
@ Forum /forum {@2 3 4 5 6} wideonly
611641
@ Chat /chat C wideonly
612642
@ Tickets /ticket r wideonly
613643
@ Wiki /wiki j wideonly
614
-@ Setup /setup s desktoponly
644
+@ Admin /setup {a s} desktoponly
615645
@ Logout /logout L wideonly
616646
@ Login /login !L wideonly
617647
;
618648
619649
/*
@@ -705,11 +735,11 @@
705735
Th_Store("release_version", RELEASE_VERSION);
706736
Th_Store("manifest_version", MANIFEST_VERSION);
707737
Th_Store("manifest_date", MANIFEST_DATE);
708738
Th_Store("compiler_name", COMPILER_NAME);
709739
Th_Store("mainmenu", style_get_mainmenu());
710
- url_var("stylesheet", "css", "style.css");
740
+ stylesheet_url_var();
711741
image_url_var("logo");
712742
image_url_var("background");
713743
if( !login_is_nobody() ){
714744
Th_Store("login", g.zLogin);
715745
}
@@ -1011,10 +1041,14 @@
10111041
if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
10121042
style_load_all_js_files();
10131043
@ </body>
10141044
@ </html>
10151045
}
1046
+ /* Update the user display prefs cookie if it was modified during
1047
+ ** this request.
1048
+ */
1049
+ cookie_render();
10161050
}
10171051
10181052
/*
10191053
** Begin a side-box on the right-hand side of a page. The title and
10201054
** the width of the box are given as arguments. The width is usually
10211055
--- src/style.c
+++ src/style.c
@@ -366,51 +366,79 @@
366 va_end(ap);
367 }
368 }
369
370 /*
371 ** Create a TH1 variable containing the URL for the specified config
372 ** resource. The resulting variable name will be of the form
373 ** $[zVarPrefix]_url.
 
 
 
 
 
374 */
375 static void url_var(
376 const char *zVarPrefix,
377 const char *zConfigName,
378 const char *zPageName
379 ){
380 char *zVarName = mprintf("%s_url", zVarPrefix);
381 char *zUrl = 0; /* stylesheet URL */
382 int hasBuiltin = 0; /* true for built-in page-specific CSS */
383
384 if(0==strcmp("css",zConfigName)){
385 /* Account for page-specific CSS, appending a /{{g.zPath}} to the
386 ** url only if we have a corresponding built-in page-specific CSS
387 ** file. Do not append it to all pages because we would
388 ** effectively cache-bust all pages which do not have
389 ** page-specific CSS. */
390 char * zBuiltin = mprintf("style.%s.css", g.zPath);
391 hasBuiltin = builtin_file(zBuiltin,0)!=0;
392 fossil_free(zBuiltin);
393 }
394 zUrl = mprintf("%R/%s%s%s?id=%x", zPageName,
395 hasBuiltin ? "/" : "", hasBuiltin ? g.zPath : "",
396 skin_id(zConfigName));
397 Th_Store(zVarName, zUrl);
398 fossil_free(zUrl);
399 fossil_free(zVarName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400 }
401
402 /*
403 ** Create a TH1 variable containing the URL for the specified config image.
404 ** The resulting variable name will be of the form $[zImageName]_image_url.
 
 
 
405 */
406 static void image_url_var(const char *zImageName){
407 char *zVarPrefix = mprintf("%s_image", zImageName);
408 char *zConfigName = mprintf("%s-image", zImageName);
409 url_var(zVarPrefix, zConfigName, zImageName);
410 free(zVarPrefix);
411 free(zConfigName);
 
 
 
 
 
 
412 }
413
414 /*
415 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
416 ** Javascript module, and generates HTML elements with the following IDs:
@@ -519,10 +547,11 @@
519 ** default is used instead:
520 **
521 ** default-src 'self' data:;
522 ** script-src 'self' 'nonce-$nonce';
523 ** style-src 'self' 'unsafe-inline';
 
524 **
525 ** The text '$nonce' is replaced by style_nonce() if and whereever it
526 ** occurs in the input string.
527 **
528 ** The string returned is obtained from fossil_malloc() and
@@ -530,11 +559,12 @@
530 */
531 char *style_csp(int toHeader){
532 static const char zBackupCSP[] =
533 "default-src 'self' data:; "
534 "script-src 'self' 'nonce-$nonce'; "
535 "style-src 'self' 'unsafe-inline'";
 
536 const char *zFormat;
537 Blob csp;
538 char *zNonce;
539 char *zCsp;
540 int i;
@@ -609,11 +639,11 @@
609 @ Tags /taglist o wideonly
610 @ Forum /forum {@2 3 4 5 6} wideonly
611 @ Chat /chat C wideonly
612 @ Tickets /ticket r wideonly
613 @ Wiki /wiki j wideonly
614 @ Setup /setup s desktoponly
615 @ Logout /logout L wideonly
616 @ Login /login !L wideonly
617 ;
618
619 /*
@@ -705,11 +735,11 @@
705 Th_Store("release_version", RELEASE_VERSION);
706 Th_Store("manifest_version", MANIFEST_VERSION);
707 Th_Store("manifest_date", MANIFEST_DATE);
708 Th_Store("compiler_name", COMPILER_NAME);
709 Th_Store("mainmenu", style_get_mainmenu());
710 url_var("stylesheet", "css", "style.css");
711 image_url_var("logo");
712 image_url_var("background");
713 if( !login_is_nobody() ){
714 Th_Store("login", g.zLogin);
715 }
@@ -1011,10 +1041,14 @@
1011 if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
1012 style_load_all_js_files();
1013 @ </body>
1014 @ </html>
1015 }
 
 
 
 
1016 }
1017
1018 /*
1019 ** Begin a side-box on the right-hand side of a page. The title and
1020 ** the width of the box are given as arguments. The width is usually
1021
--- src/style.c
+++ src/style.c
@@ -366,51 +366,79 @@
366 va_end(ap);
367 }
368 }
369
370 /*
371 ** Create a TH1 variable containing the URL for the stylesheet.
372 **
373 ** The name of the new variable will be "stylesheet_url".
374 **
375 ** The value will be a URL for accessing the appropriate stylesheet.
376 ** This URL will include query parameters such as "id=" and "once&skin="
377 ** to cause the correct stylesheet to be loaded after a skin change
378 ** or after a change to the stylesheet.
379 */
380 static void stylesheet_url_var(void){
381 char *zBuiltin; /* Auxiliary page-specific CSS page */
382 Blob url; /* The URL */
383
384 /* Initialize the URL to its baseline */
385 url = empty_blob;
386 blob_appendf(&url, "%R/style.css");
387
388 /* If page-specific CSS exists for the current page, then append
389 ** the pathname for the page-specific CSS. The default CSS is
390 **
391 ** /style.css
392 **
393 ** But for the "/wikiedit" page (to name but one example), we
394 ** append a path as follows:
395 **
396 ** /style.css/wikiedit
397 **
398 ** The /style.css page (implemented below) will detect this extra "wikiedit"
399 ** path information and include the page-specific CSS along with the
400 ** default CSS when it delivers the page.
401 */
402 zBuiltin = mprintf("style.%s.css", g.zPath);
403 if( builtin_file(zBuiltin,0)!=0 ){
404 blob_appendf(&url, "/%s", g.zPath);
405 }
406 fossil_free(zBuiltin);
407
408 /* Add query parameters that will change whenever the skin changes
409 ** or after any updates to the CSS files
410 */
411 blob_appendf(&url, "?id=%x", skin_id("css"));
412 if( P("once")!=0 && P("skin")!=0 ){
413 blob_appendf(&url, "&skin=%s&once", skin_in_use());
414 }
415
416 /* Generate the CSS URL variable */
417 Th_Store("stylesheet_url", blob_str(&url));
418 blob_reset(&url);
419 }
420
421 /*
422 ** Create a TH1 variable containing the URL for the specified image.
423 ** The resulting variable name will be of the form $[zImageName]_image_url.
424 ** The value will be a URL that includes an id= query parameter that
425 ** changes if the underlying resource changes or if a different skin
426 ** is selected.
427 */
428 static void image_url_var(const char *zImageName){
429 char *zVarName; /* Name of the new TH1 variable */
430 char *zResource; /* Name of CONFIG entry holding content */
431 char *zUrl; /* The URL */
432
433 zResource = mprintf("%s-image", zImageName);
434 zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource));
435 free(zResource);
436 zVarName = mprintf("%s_image_url", zImageName);
437 Th_Store(zVarName, zUrl);
438 free(zVarName);
439 free(zUrl);
440 }
441
442 /*
443 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
444 ** Javascript module, and generates HTML elements with the following IDs:
@@ -519,10 +547,11 @@
547 ** default is used instead:
548 **
549 ** default-src 'self' data:;
550 ** script-src 'self' 'nonce-$nonce';
551 ** style-src 'self' 'unsafe-inline';
552 ** img-src * data:;
553 **
554 ** The text '$nonce' is replaced by style_nonce() if and whereever it
555 ** occurs in the input string.
556 **
557 ** The string returned is obtained from fossil_malloc() and
@@ -530,11 +559,12 @@
559 */
560 char *style_csp(int toHeader){
561 static const char zBackupCSP[] =
562 "default-src 'self' data:; "
563 "script-src 'self' 'nonce-$nonce'; "
564 "style-src 'self' 'unsafe-inline'; "
565 "img-src * data:";
566 const char *zFormat;
567 Blob csp;
568 char *zNonce;
569 char *zCsp;
570 int i;
@@ -609,11 +639,11 @@
639 @ Tags /taglist o wideonly
640 @ Forum /forum {@2 3 4 5 6} wideonly
641 @ Chat /chat C wideonly
642 @ Tickets /ticket r wideonly
643 @ Wiki /wiki j wideonly
644 @ Admin /setup {a s} desktoponly
645 @ Logout /logout L wideonly
646 @ Login /login !L wideonly
647 ;
648
649 /*
@@ -705,11 +735,11 @@
735 Th_Store("release_version", RELEASE_VERSION);
736 Th_Store("manifest_version", MANIFEST_VERSION);
737 Th_Store("manifest_date", MANIFEST_DATE);
738 Th_Store("compiler_name", COMPILER_NAME);
739 Th_Store("mainmenu", style_get_mainmenu());
740 stylesheet_url_var();
741 image_url_var("logo");
742 image_url_var("background");
743 if( !login_is_nobody() ){
744 Th_Store("login", g.zLogin);
745 }
@@ -1011,10 +1041,14 @@
1041 if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
1042 style_load_all_js_files();
1043 @ </body>
1044 @ </html>
1045 }
1046 /* Update the user display prefs cookie if it was modified during
1047 ** this request.
1048 */
1049 cookie_render();
1050 }
1051
1052 /*
1053 ** Begin a side-box on the right-hand side of a page. The title and
1054 ** the width of the box are given as arguments. The width is usually
1055
-1
--- src/tag.c
+++ src/tag.c
@@ -755,11 +755,10 @@
755755
756756
style_header("Tagged Check-ins");
757757
style_submenu_element("List", "taglist");
758758
login_anonymous_available();
759759
timeline_ss_submenu();
760
- cookie_render();
761760
@ <h2>Check-ins with non-propagating tags:</h2>
762761
blob_append(&sql, timeline_query_for_www(), -1);
763762
blob_append_sql(&sql,
764763
"AND blob.rid IN (SELECT rid FROM tagxref"
765764
" WHERE tagtype=1 AND srcid>0"
766765
--- src/tag.c
+++ src/tag.c
@@ -755,11 +755,10 @@
755
756 style_header("Tagged Check-ins");
757 style_submenu_element("List", "taglist");
758 login_anonymous_available();
759 timeline_ss_submenu();
760 cookie_render();
761 @ <h2>Check-ins with non-propagating tags:</h2>
762 blob_append(&sql, timeline_query_for_www(), -1);
763 blob_append_sql(&sql,
764 "AND blob.rid IN (SELECT rid FROM tagxref"
765 " WHERE tagtype=1 AND srcid>0"
766
--- src/tag.c
+++ src/tag.c
@@ -755,11 +755,10 @@
755
756 style_header("Tagged Check-ins");
757 style_submenu_element("List", "taglist");
758 login_anonymous_available();
759 timeline_ss_submenu();
 
760 @ <h2>Check-ins with non-propagating tags:</h2>
761 blob_append(&sql, timeline_query_for_www(), -1);
762 blob_append_sql(&sql,
763 "AND blob.rid IN (SELECT rid FROM tagxref"
764 " WHERE tagtype=1 AND srcid>0"
765
+1 -1
--- src/tar.c
+++ src/tar.c
@@ -425,11 +425,11 @@
425425
** COMMAND: test-tarball
426426
**
427427
** Generate a GZIP-compressed tarball in the file given by the first argument
428428
** that contains files given in the second and subsequent arguments.
429429
**
430
-** -h, --dereference Follow symlinks; archive the files they point to.
430
+** -h|--dereference Follow symlinks and archive the files they point to
431431
*/
432432
void test_tarball_cmd(void){
433433
int i;
434434
Blob zip;
435435
int eFType = SymFILE;
436436
--- src/tar.c
+++ src/tar.c
@@ -425,11 +425,11 @@
425 ** COMMAND: test-tarball
426 **
427 ** Generate a GZIP-compressed tarball in the file given by the first argument
428 ** that contains files given in the second and subsequent arguments.
429 **
430 ** -h, --dereference Follow symlinks; archive the files they point to.
431 */
432 void test_tarball_cmd(void){
433 int i;
434 Blob zip;
435 int eFType = SymFILE;
436
--- src/tar.c
+++ src/tar.c
@@ -425,11 +425,11 @@
425 ** COMMAND: test-tarball
426 **
427 ** Generate a GZIP-compressed tarball in the file given by the first argument
428 ** that contains files given in the second and subsequent arguments.
429 **
430 ** -h|--dereference Follow symlinks and archive the files they point to
431 */
432 void test_tarball_cmd(void){
433 int i;
434 Blob zip;
435 int eFType = SymFILE;
436
--- src/timeline.c
+++ src/timeline.c
@@ -1754,11 +1754,10 @@
17541754
cgi_set_parameter("y", zType);
17551755
}
17561756
if( zType[0]=='a' || zType[0]=='c' ){
17571757
cookie_write_parameter("y","y",zType);
17581758
}
1759
- cookie_render();
17601759
17611760
/* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
17621761
if( P("cf")!=0 ){
17631762
zCirca = db_text(0,
17641763
"SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
17651764
--- src/timeline.c
+++ src/timeline.c
@@ -1754,11 +1754,10 @@
1754 cgi_set_parameter("y", zType);
1755 }
1756 if( zType[0]=='a' || zType[0]=='c' ){
1757 cookie_write_parameter("y","y",zType);
1758 }
1759 cookie_render();
1760
1761 /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
1762 if( P("cf")!=0 ){
1763 zCirca = db_text(0,
1764 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
1765
--- src/timeline.c
+++ src/timeline.c
@@ -1754,11 +1754,10 @@
1754 cgi_set_parameter("y", zType);
1755 }
1756 if( zType[0]=='a' || zType[0]=='c' ){
1757 cookie_write_parameter("y","y",zType);
1758 }
 
1759
1760 /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
1761 if( P("cf")!=0 ){
1762 zCirca = db_text(0,
1763 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
1764
+1 -1
--- src/tkt.c
+++ src/tkt.c
@@ -1239,11 +1239,11 @@
12391239
** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS?
12401240
**
12411241
** Options:
12421242
** -l|--limit LIMITCHAR
12431243
** -q|--quote
1244
-** -R|--repository FILE
1244
+** -R|--repository REPO
12451245
**
12461246
** Run the ticket report, identified by the report format title
12471247
** used in the GUI. The data is written as flat file on stdout,
12481248
** using TAB as separator. The separator can be changed using
12491249
** the -l or --limit option.
12501250
--- src/tkt.c
+++ src/tkt.c
@@ -1239,11 +1239,11 @@
1239 ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS?
1240 **
1241 ** Options:
1242 ** -l|--limit LIMITCHAR
1243 ** -q|--quote
1244 ** -R|--repository FILE
1245 **
1246 ** Run the ticket report, identified by the report format title
1247 ** used in the GUI. The data is written as flat file on stdout,
1248 ** using TAB as separator. The separator can be changed using
1249 ** the -l or --limit option.
1250
--- src/tkt.c
+++ src/tkt.c
@@ -1239,11 +1239,11 @@
1239 ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS?
1240 **
1241 ** Options:
1242 ** -l|--limit LIMITCHAR
1243 ** -q|--quote
1244 ** -R|--repository REPO
1245 **
1246 ** Run the ticket report, identified by the report format title
1247 ** used in the GUI. The data is written as flat file on stdout,
1248 ** using TAB as separator. The separator can be changed using
1249 ** the -l or --limit option.
1250
+1 -1
--- src/undo.c
+++ src/undo.c
@@ -462,11 +462,11 @@
462462
**
463463
** Future versions of Fossil might add new commands to the set of commands
464464
** that are undoable.
465465
**
466466
** Options:
467
-** -n|--dry-run do not make changes but show what would be done
467
+** -n|--dry-run Do not make changes but show what would be done
468468
**
469469
** See also: [[commit]], [[status]]
470470
*/
471471
void undo_cmd(void){
472472
int isRedo = g.argv[1][0]=='r';
473473
--- src/undo.c
+++ src/undo.c
@@ -462,11 +462,11 @@
462 **
463 ** Future versions of Fossil might add new commands to the set of commands
464 ** that are undoable.
465 **
466 ** Options:
467 ** -n|--dry-run do not make changes but show what would be done
468 **
469 ** See also: [[commit]], [[status]]
470 */
471 void undo_cmd(void){
472 int isRedo = g.argv[1][0]=='r';
473
--- src/undo.c
+++ src/undo.c
@@ -462,11 +462,11 @@
462 **
463 ** Future versions of Fossil might add new commands to the set of commands
464 ** that are undoable.
465 **
466 ** Options:
467 ** -n|--dry-run Do not make changes but show what would be done
468 **
469 ** See also: [[commit]], [[status]]
470 */
471 void undo_cmd(void){
472 int isRedo = g.argv[1][0]=='r';
473
--- src/unversioned.c
+++ src/unversioned.c
@@ -291,11 +291,11 @@
291291
**
292292
** Options:
293293
**
294294
** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add",
295295
** "edit", "remove", and "touch" subcommands.
296
-** -R|--repository FILE Use FILE as the repository
296
+** -R|--repository REPO Use FILE as the repository
297297
*/
298298
void unversioned_cmd(void){
299299
const char *zCmd;
300300
int nCmd;
301301
const char *zMtime = find_option("mtime", 0, 1);
302302
--- src/unversioned.c
+++ src/unversioned.c
@@ -291,11 +291,11 @@
291 **
292 ** Options:
293 **
294 ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add",
295 ** "edit", "remove", and "touch" subcommands.
296 ** -R|--repository FILE Use FILE as the repository
297 */
298 void unversioned_cmd(void){
299 const char *zCmd;
300 int nCmd;
301 const char *zMtime = find_option("mtime", 0, 1);
302
--- src/unversioned.c
+++ src/unversioned.c
@@ -291,11 +291,11 @@
291 **
292 ** Options:
293 **
294 ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add",
295 ** "edit", "remove", and "touch" subcommands.
296 ** -R|--repository REPO Use FILE as the repository
297 */
298 void unversioned_cmd(void){
299 const char *zCmd;
300 int nCmd;
301 const char *zMtime = find_option("mtime", 0, 1);
302
+1 -1
--- src/user.c
+++ src/user.c
@@ -326,11 +326,11 @@
326326
}
327327
328328
/*
329329
** COMMAND: user*
330330
**
331
-** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE?
331
+** Usage: %fossil user SUBCOMMAND ... ?-R|--repository REPO?
332332
**
333333
** Run various subcommands on users of the open repository or of
334334
** the repository identified by the -R or --repository option.
335335
**
336336
** > fossil user capabilities USERNAME ?STRING?
337337
--- src/user.c
+++ src/user.c
@@ -326,11 +326,11 @@
326 }
327
328 /*
329 ** COMMAND: user*
330 **
331 ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE?
332 **
333 ** Run various subcommands on users of the open repository or of
334 ** the repository identified by the -R or --repository option.
335 **
336 ** > fossil user capabilities USERNAME ?STRING?
337
--- src/user.c
+++ src/user.c
@@ -326,11 +326,11 @@
326 }
327
328 /*
329 ** COMMAND: user*
330 **
331 ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository REPO?
332 **
333 ** Run various subcommands on users of the open repository or of
334 ** the repository identified by the -R or --repository option.
335 **
336 ** > fossil user capabilities USERNAME ?STRING?
337
+3 -3
--- src/wiki.c
+++ src/wiki.c
@@ -768,11 +768,11 @@
768768
**
769769
** Output JSON format:
770770
**
771771
** { name: "page name",
772772
** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
773
-** mimetype: "mime type",
773
+** mimetype: "mimetype",
774774
** version: UUID string or null for a sandbox page,
775775
** parent: "parent uuid" or null if no parent,
776776
** isDeleted: true if the page has no content (is "deleted")
777777
** else not set (making it "falsy" in JS),
778778
** content: "page content" (only if includeContent is true)
@@ -836,11 +836,11 @@
836836
** Ajax route handler for /wikiajax/save.
837837
**
838838
** URL params:
839839
**
840840
** page = the wiki page name.
841
-** mimetype = content mime type.
841
+** mimetype = content mimetype.
842842
** content = page content. Fossil considers an empty page to
843843
** be "deleted".
844844
** isnew = 1 if the page is to be newly-created, else 0 or
845845
** not send.
846846
**
@@ -2180,11 +2180,11 @@
21802180
if( rid>0 ){
21812181
pWiki = manifest_get(rid, CFTYPE_EVENT, 0);
21822182
}
21832183
}
21842184
if( !zMimeType || !*zMimeType ){
2185
- /* Try to deduce the mime type based on the prior version. */
2185
+ /* Try to deduce the mimetype based on the prior version. */
21862186
if(isSandbox){
21872187
zMimeType =
21882188
wiki_filter_mimetypes(db_get("sandbox-mimetype",
21892189
"text/x-fossil-wiki"));
21902190
}else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){
21912191
--- src/wiki.c
+++ src/wiki.c
@@ -768,11 +768,11 @@
768 **
769 ** Output JSON format:
770 **
771 ** { name: "page name",
772 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
773 ** mimetype: "mime type",
774 ** version: UUID string or null for a sandbox page,
775 ** parent: "parent uuid" or null if no parent,
776 ** isDeleted: true if the page has no content (is "deleted")
777 ** else not set (making it "falsy" in JS),
778 ** content: "page content" (only if includeContent is true)
@@ -836,11 +836,11 @@
836 ** Ajax route handler for /wikiajax/save.
837 **
838 ** URL params:
839 **
840 ** page = the wiki page name.
841 ** mimetype = content mime type.
842 ** content = page content. Fossil considers an empty page to
843 ** be "deleted".
844 ** isnew = 1 if the page is to be newly-created, else 0 or
845 ** not send.
846 **
@@ -2180,11 +2180,11 @@
2180 if( rid>0 ){
2181 pWiki = manifest_get(rid, CFTYPE_EVENT, 0);
2182 }
2183 }
2184 if( !zMimeType || !*zMimeType ){
2185 /* Try to deduce the mime type based on the prior version. */
2186 if(isSandbox){
2187 zMimeType =
2188 wiki_filter_mimetypes(db_get("sandbox-mimetype",
2189 "text/x-fossil-wiki"));
2190 }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){
2191
--- src/wiki.c
+++ src/wiki.c
@@ -768,11 +768,11 @@
768 **
769 ** Output JSON format:
770 **
771 ** { name: "page name",
772 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
773 ** mimetype: "mimetype",
774 ** version: UUID string or null for a sandbox page,
775 ** parent: "parent uuid" or null if no parent,
776 ** isDeleted: true if the page has no content (is "deleted")
777 ** else not set (making it "falsy" in JS),
778 ** content: "page content" (only if includeContent is true)
@@ -836,11 +836,11 @@
836 ** Ajax route handler for /wikiajax/save.
837 **
838 ** URL params:
839 **
840 ** page = the wiki page name.
841 ** mimetype = content mimetype.
842 ** content = page content. Fossil considers an empty page to
843 ** be "deleted".
844 ** isnew = 1 if the page is to be newly-created, else 0 or
845 ** not send.
846 **
@@ -2180,11 +2180,11 @@
2180 if( rid>0 ){
2181 pWiki = manifest_get(rid, CFTYPE_EVENT, 0);
2182 }
2183 }
2184 if( !zMimeType || !*zMimeType ){
2185 /* Try to deduce the mimetype based on the prior version. */
2186 if(isSandbox){
2187 zMimeType =
2188 wiki_filter_mimetypes(db_get("sandbox-mimetype",
2189 "text/x-fossil-wiki"));
2190 }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){
2191
+3 -3
--- src/wiki.c
+++ src/wiki.c
@@ -768,11 +768,11 @@
768768
**
769769
** Output JSON format:
770770
**
771771
** { name: "page name",
772772
** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
773
-** mimetype: "mime type",
773
+** mimetype: "mimetype",
774774
** version: UUID string or null for a sandbox page,
775775
** parent: "parent uuid" or null if no parent,
776776
** isDeleted: true if the page has no content (is "deleted")
777777
** else not set (making it "falsy" in JS),
778778
** content: "page content" (only if includeContent is true)
@@ -836,11 +836,11 @@
836836
** Ajax route handler for /wikiajax/save.
837837
**
838838
** URL params:
839839
**
840840
** page = the wiki page name.
841
-** mimetype = content mime type.
841
+** mimetype = content mimetype.
842842
** content = page content. Fossil considers an empty page to
843843
** be "deleted".
844844
** isnew = 1 if the page is to be newly-created, else 0 or
845845
** not send.
846846
**
@@ -2180,11 +2180,11 @@
21802180
if( rid>0 ){
21812181
pWiki = manifest_get(rid, CFTYPE_EVENT, 0);
21822182
}
21832183
}
21842184
if( !zMimeType || !*zMimeType ){
2185
- /* Try to deduce the mime type based on the prior version. */
2185
+ /* Try to deduce the mimetype based on the prior version. */
21862186
if(isSandbox){
21872187
zMimeType =
21882188
wiki_filter_mimetypes(db_get("sandbox-mimetype",
21892189
"text/x-fossil-wiki"));
21902190
}else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){
21912191
--- src/wiki.c
+++ src/wiki.c
@@ -768,11 +768,11 @@
768 **
769 ** Output JSON format:
770 **
771 ** { name: "page name",
772 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
773 ** mimetype: "mime type",
774 ** version: UUID string or null for a sandbox page,
775 ** parent: "parent uuid" or null if no parent,
776 ** isDeleted: true if the page has no content (is "deleted")
777 ** else not set (making it "falsy" in JS),
778 ** content: "page content" (only if includeContent is true)
@@ -836,11 +836,11 @@
836 ** Ajax route handler for /wikiajax/save.
837 **
838 ** URL params:
839 **
840 ** page = the wiki page name.
841 ** mimetype = content mime type.
842 ** content = page content. Fossil considers an empty page to
843 ** be "deleted".
844 ** isnew = 1 if the page is to be newly-created, else 0 or
845 ** not send.
846 **
@@ -2180,11 +2180,11 @@
2180 if( rid>0 ){
2181 pWiki = manifest_get(rid, CFTYPE_EVENT, 0);
2182 }
2183 }
2184 if( !zMimeType || !*zMimeType ){
2185 /* Try to deduce the mime type based on the prior version. */
2186 if(isSandbox){
2187 zMimeType =
2188 wiki_filter_mimetypes(db_get("sandbox-mimetype",
2189 "text/x-fossil-wiki"));
2190 }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){
2191
--- src/wiki.c
+++ src/wiki.c
@@ -768,11 +768,11 @@
768 **
769 ** Output JSON format:
770 **
771 ** { name: "page name",
772 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
773 ** mimetype: "mimetype",
774 ** version: UUID string or null for a sandbox page,
775 ** parent: "parent uuid" or null if no parent,
776 ** isDeleted: true if the page has no content (is "deleted")
777 ** else not set (making it "falsy" in JS),
778 ** content: "page content" (only if includeContent is true)
@@ -836,11 +836,11 @@
836 ** Ajax route handler for /wikiajax/save.
837 **
838 ** URL params:
839 **
840 ** page = the wiki page name.
841 ** mimetype = content mimetype.
842 ** content = page content. Fossil considers an empty page to
843 ** be "deleted".
844 ** isnew = 1 if the page is to be newly-created, else 0 or
845 ** not send.
846 **
@@ -2180,11 +2180,11 @@
2180 if( rid>0 ){
2181 pWiki = manifest_get(rid, CFTYPE_EVENT, 0);
2182 }
2183 }
2184 if( !zMimeType || !*zMimeType ){
2185 /* Try to deduce the mimetype based on the prior version. */
2186 if(isSandbox){
2187 zMimeType =
2188 wiki_filter_mimetypes(db_get("sandbox-mimetype",
2189 "text/x-fossil-wiki"));
2190 }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){
2191
+1 -1
--- src/winhttp.c
+++ src/winhttp.c
@@ -972,11 +972,11 @@
972972
** -P|--port TCPPORT
973973
**
974974
** Specifies the TCP port (default port is 8080) on which the
975975
** server should listen.
976976
**
977
-** -R|--repository REPOSITORY
977
+** -R|--repository REPO
978978
**
979979
** Specifies the name of the repository to be served.
980980
** The repository option may be omitted if the working directory
981981
** is within an open checkout.
982982
** The REPOSITORY can be a directory (aka folder) that contains
983983
--- src/winhttp.c
+++ src/winhttp.c
@@ -972,11 +972,11 @@
972 ** -P|--port TCPPORT
973 **
974 ** Specifies the TCP port (default port is 8080) on which the
975 ** server should listen.
976 **
977 ** -R|--repository REPOSITORY
978 **
979 ** Specifies the name of the repository to be served.
980 ** The repository option may be omitted if the working directory
981 ** is within an open checkout.
982 ** The REPOSITORY can be a directory (aka folder) that contains
983
--- src/winhttp.c
+++ src/winhttp.c
@@ -972,11 +972,11 @@
972 ** -P|--port TCPPORT
973 **
974 ** Specifies the TCP port (default port is 8080) on which the
975 ** server should listen.
976 **
977 ** -R|--repository REPO
978 **
979 ** Specifies the name of the repository to be served.
980 ** The repository option may be omitted if the working directory
981 ** is within an open checkout.
982 ** The REPOSITORY can be a directory (aka folder) that contains
983
--- test/release-checklist.wiki
+++ test/release-checklist.wiki
@@ -2,10 +2,15 @@
22
33
This file describes the testing procedures for Fossil prior to an
44
official release.
55
66
<ol>
7
+<li><p>
8
+From within a checkout of the Fossil tree, display this file with
9
+the command "<b>fossil ui --page doc/ckout/test/release-checklist.wiki</b>".
10
+That is the only way the links below will work.
11
+
712
<li><p>
813
From a private directory (not the source tree) run
914
"<b>tclsh $SRC/test/tester.tcl $FOSSIL</b>" where $FOSSIL is the
1015
name of the executable under test and $SRC is the source tree.
1116
Verify that there are no errors.
@@ -91,10 +96,17 @@
9196
9297
<li><p>
9398
Use the release candidate version of fossil in production on the
9499
[http://fossil-scm.org/] website for at least 48 hours (without
95100
incident) prior to making the release official.
101
+
102
+<li><p>
103
+Verify that the minimum SQLite version requirement is up-to-date:
104
+<ol type="a">
105
+<li> Check the version number in the line starting "define MINIMUM_SQLITE_VERSION" near the top of [/file?name=auto.def&ci=tip | auto.def]
106
+<li> Check the output of <b>./configure --print-minimum-sqlite-version</b>
107
+</ol>
96108
97109
<li><p>
98110
Verify that the [../www/changes.wiki | Change Log] is correct and
99111
up-to-date.
100112
</ol>
101113
--- test/release-checklist.wiki
+++ test/release-checklist.wiki
@@ -2,10 +2,15 @@
2
3 This file describes the testing procedures for Fossil prior to an
4 official release.
5
6 <ol>
 
 
 
 
 
7 <li><p>
8 From a private directory (not the source tree) run
9 "<b>tclsh $SRC/test/tester.tcl $FOSSIL</b>" where $FOSSIL is the
10 name of the executable under test and $SRC is the source tree.
11 Verify that there are no errors.
@@ -91,10 +96,17 @@
91
92 <li><p>
93 Use the release candidate version of fossil in production on the
94 [http://fossil-scm.org/] website for at least 48 hours (without
95 incident) prior to making the release official.
 
 
 
 
 
 
 
96
97 <li><p>
98 Verify that the [../www/changes.wiki | Change Log] is correct and
99 up-to-date.
100 </ol>
101
--- test/release-checklist.wiki
+++ test/release-checklist.wiki
@@ -2,10 +2,15 @@
2
3 This file describes the testing procedures for Fossil prior to an
4 official release.
5
6 <ol>
7 <li><p>
8 From within a checkout of the Fossil tree, display this file with
9 the command "<b>fossil ui --page doc/ckout/test/release-checklist.wiki</b>".
10 That is the only way the links below will work.
11
12 <li><p>
13 From a private directory (not the source tree) run
14 "<b>tclsh $SRC/test/tester.tcl $FOSSIL</b>" where $FOSSIL is the
15 name of the executable under test and $SRC is the source tree.
16 Verify that there are no errors.
@@ -91,10 +96,17 @@
96
97 <li><p>
98 Use the release candidate version of fossil in production on the
99 [http://fossil-scm.org/] website for at least 48 hours (without
100 incident) prior to making the release official.
101
102 <li><p>
103 Verify that the minimum SQLite version requirement is up-to-date:
104 <ol type="a">
105 <li> Check the version number in the line starting "define MINIMUM_SQLITE_VERSION" near the top of [/file?name=auto.def&ci=tip | auto.def]
106 <li> Check the output of <b>./configure --print-minimum-sqlite-version</b>
107 </ol>
108
109 <li><p>
110 Verify that the [../www/changes.wiki | Change Log] is correct and
111 up-to-date.
112 </ol>
113
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -634,10 +634,11 @@
634634
$(SRCDIR)/fossil.bootstrap.js \
635635
$(SRCDIR)/fossil.confirmer.js \
636636
$(SRCDIR)/fossil.copybutton.js \
637637
$(SRCDIR)/fossil.dom.js \
638638
$(SRCDIR)/fossil.fetch.js \
639
+ $(SRCDIR)/fossil.info-diff.js \
639640
$(SRCDIR)/fossil.numbered-lines.js \
640641
$(SRCDIR)/fossil.page.fileedit.js \
641642
$(SRCDIR)/fossil.page.forumpost.js \
642643
$(SRCDIR)/fossil.page.pikchrshow.js \
643644
$(SRCDIR)/fossil.page.wikiedit.js \
644645
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -634,10 +634,11 @@
634 $(SRCDIR)/fossil.bootstrap.js \
635 $(SRCDIR)/fossil.confirmer.js \
636 $(SRCDIR)/fossil.copybutton.js \
637 $(SRCDIR)/fossil.dom.js \
638 $(SRCDIR)/fossil.fetch.js \
 
639 $(SRCDIR)/fossil.numbered-lines.js \
640 $(SRCDIR)/fossil.page.fileedit.js \
641 $(SRCDIR)/fossil.page.forumpost.js \
642 $(SRCDIR)/fossil.page.pikchrshow.js \
643 $(SRCDIR)/fossil.page.wikiedit.js \
644
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -634,10 +634,11 @@
634 $(SRCDIR)/fossil.bootstrap.js \
635 $(SRCDIR)/fossil.confirmer.js \
636 $(SRCDIR)/fossil.copybutton.js \
637 $(SRCDIR)/fossil.dom.js \
638 $(SRCDIR)/fossil.fetch.js \
639 $(SRCDIR)/fossil.info-diff.js \
640 $(SRCDIR)/fossil.numbered-lines.js \
641 $(SRCDIR)/fossil.page.fileedit.js \
642 $(SRCDIR)/fossil.page.forumpost.js \
643 $(SRCDIR)/fossil.page.pikchrshow.js \
644 $(SRCDIR)/fossil.page.wikiedit.js \
645
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -559,10 +559,11 @@
559559
"$(SRCDIR)\fossil.bootstrap.js" \
560560
"$(SRCDIR)\fossil.confirmer.js" \
561561
"$(SRCDIR)\fossil.copybutton.js" \
562562
"$(SRCDIR)\fossil.dom.js" \
563563
"$(SRCDIR)\fossil.fetch.js" \
564
+ "$(SRCDIR)\fossil.info-diff.js" \
564565
"$(SRCDIR)\fossil.numbered-lines.js" \
565566
"$(SRCDIR)\fossil.page.fileedit.js" \
566567
"$(SRCDIR)\fossil.page.forumpost.js" \
567568
"$(SRCDIR)\fossil.page.pikchrshow.js" \
568569
"$(SRCDIR)\fossil.page.wikiedit.js" \
@@ -1164,10 +1165,11 @@
11641165
echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
11651166
echo "$(SRCDIR)\fossil.confirmer.js" >> $@
11661167
echo "$(SRCDIR)\fossil.copybutton.js" >> $@
11671168
echo "$(SRCDIR)\fossil.dom.js" >> $@
11681169
echo "$(SRCDIR)\fossil.fetch.js" >> $@
1170
+ echo "$(SRCDIR)\fossil.info-diff.js" >> $@
11691171
echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@
11701172
echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
11711173
echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
11721174
echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
11731175
echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
11741176
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -559,10 +559,11 @@
559 "$(SRCDIR)\fossil.bootstrap.js" \
560 "$(SRCDIR)\fossil.confirmer.js" \
561 "$(SRCDIR)\fossil.copybutton.js" \
562 "$(SRCDIR)\fossil.dom.js" \
563 "$(SRCDIR)\fossil.fetch.js" \
 
564 "$(SRCDIR)\fossil.numbered-lines.js" \
565 "$(SRCDIR)\fossil.page.fileedit.js" \
566 "$(SRCDIR)\fossil.page.forumpost.js" \
567 "$(SRCDIR)\fossil.page.pikchrshow.js" \
568 "$(SRCDIR)\fossil.page.wikiedit.js" \
@@ -1164,10 +1165,11 @@
1164 echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
1165 echo "$(SRCDIR)\fossil.confirmer.js" >> $@
1166 echo "$(SRCDIR)\fossil.copybutton.js" >> $@
1167 echo "$(SRCDIR)\fossil.dom.js" >> $@
1168 echo "$(SRCDIR)\fossil.fetch.js" >> $@
 
1169 echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@
1170 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1171 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1172 echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
1173 echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
1174
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -559,10 +559,11 @@
559 "$(SRCDIR)\fossil.bootstrap.js" \
560 "$(SRCDIR)\fossil.confirmer.js" \
561 "$(SRCDIR)\fossil.copybutton.js" \
562 "$(SRCDIR)\fossil.dom.js" \
563 "$(SRCDIR)\fossil.fetch.js" \
564 "$(SRCDIR)\fossil.info-diff.js" \
565 "$(SRCDIR)\fossil.numbered-lines.js" \
566 "$(SRCDIR)\fossil.page.fileedit.js" \
567 "$(SRCDIR)\fossil.page.forumpost.js" \
568 "$(SRCDIR)\fossil.page.pikchrshow.js" \
569 "$(SRCDIR)\fossil.page.wikiedit.js" \
@@ -1164,10 +1165,11 @@
1165 echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
1166 echo "$(SRCDIR)\fossil.confirmer.js" >> $@
1167 echo "$(SRCDIR)\fossil.copybutton.js" >> $@
1168 echo "$(SRCDIR)\fossil.dom.js" >> $@
1169 echo "$(SRCDIR)\fossil.fetch.js" >> $@
1170 echo "$(SRCDIR)\fossil.info-diff.js" >> $@
1171 echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@
1172 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1173 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1174 echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
1175 echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
1176
+14 -6
--- www/backup.md
+++ www/backup.md
@@ -231,17 +231,25 @@
231231
232232
This requires OpenSSL 1.1 or higher. If you’re on 1.0 or older, you
233233
won’t have the `-pbkdf2` and `-iter` options, and you may have to choose
234234
a different cipher algorithm; both changes are likely to weaken the
235235
encryption significantly, so you should install a newer version rather
236
-than work around the lack of these features. If you’re on macOS, which
237
-still ships 1.0 as of the time of this writing, [Homebrew][hb] offers
238
-the current version of OpenSSL, but to avoid a conflict with the platform
239
-version, it’s [unlinked][hbul] by default, so you have to give an explicit
240
-path to its “cellar” directory:
236
+than work around the lack of these features.
237
+
238
+At the time of this writing — 2021.02.26 — macOS 11 (BigSur) ships an
239
+outdated fork of OpenSSL 1.0 called [LibreSSL][lssl] that lacks this
240
+capability. Until Apple redresses this lack, we recommend use of the
241
+[Homebrew][hb] OpenSSL package rather than give up on the security
242
+afforded by use of configurable-iteration PBKDF2 in OpenSSL 1.1 and up,
243
+later backported to LibreSSL 2.9.1 and up. To avoid a conflict with the
244
+platform version, Homebrew’s installation is [unlinked][hbul] by
245
+default, so you have to give an explicit path to it, one of:
246
+
247
+ /usr/local/opt/openssl/bin/openssl ... # Intel x86 Macs
248
+ /opt/homebrew/opt/openssl/bin/openssl ... # ARM Macs (“Apple silicon”)
241249
242
- /usr/local/Cellar/openssl\@1.1/1.1.1g/bin/openssl ...
250
+[lssl]: https://www.libressl.org/
243251
244252
245253
## <a id="rest"></a> Restoring From An Encrypted Backup
246254
247255
The “restore” script for the above fragment is basically an inverse of
248256
--- www/backup.md
+++ www/backup.md
@@ -231,17 +231,25 @@
231
232 This requires OpenSSL 1.1 or higher. If you’re on 1.0 or older, you
233 won’t have the `-pbkdf2` and `-iter` options, and you may have to choose
234 a different cipher algorithm; both changes are likely to weaken the
235 encryption significantly, so you should install a newer version rather
236 than work around the lack of these features. If you’re on macOS, which
237 still ships 1.0 as of the time of this writing, [Homebrew][hb] offers
238 the current version of OpenSSL, but to avoid a conflict with the platform
239 version, it’s [unlinked][hbul] by default, so you have to give an explicit
240 path to its “cellar” directory:
 
 
 
 
 
 
 
 
241
242 /usr/local/Cellar/openssl\@1.1/1.1.1g/bin/openssl ...
243
244
245 ## <a id="rest"></a> Restoring From An Encrypted Backup
246
247 The “restore” script for the above fragment is basically an inverse of
248
--- www/backup.md
+++ www/backup.md
@@ -231,17 +231,25 @@
231
232 This requires OpenSSL 1.1 or higher. If you’re on 1.0 or older, you
233 won’t have the `-pbkdf2` and `-iter` options, and you may have to choose
234 a different cipher algorithm; both changes are likely to weaken the
235 encryption significantly, so you should install a newer version rather
236 than work around the lack of these features.
237
238 At the time of this writing — 2021.02.26 — macOS 11 (BigSur) ships an
239 outdated fork of OpenSSL 1.0 called [LibreSSL][lssl] that lacks this
240 capability. Until Apple redresses this lack, we recommend use of the
241 [Homebrew][hb] OpenSSL package rather than give up on the security
242 afforded by use of configurable-iteration PBKDF2 in OpenSSL 1.1 and up,
243 later backported to LibreSSL 2.9.1 and up. To avoid a conflict with the
244 platform version, Homebrew’s installation is [unlinked][hbul] by
245 default, so you have to give an explicit path to it, one of:
246
247 /usr/local/opt/openssl/bin/openssl ... # Intel x86 Macs
248 /opt/homebrew/opt/openssl/bin/openssl ... # ARM Macs (“Apple silicon”)
249
250 [lssl]: https://www.libressl.org/
251
252
253 ## <a id="rest"></a> Restoring From An Encrypted Backup
254
255 The “restore” script for the above fragment is basically an inverse of
256
+82 -56
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -36,16 +36,18 @@
3636
the form "<b>property:&nbsp;argument&nbsp;...</b>".
3737
The remainder of this document describes the available properties and
3838
their arguments.
3939
4040
<hr>
41
+
4142
<h2 id="repository">repository: <i>PATH</i></h2>
4243
4344
This property defines the Fossil repository that the server will use.
4445
Every Fossil CGI requires either this property or the
4546
[#directory|<b>directory:</b>] property (but not both).
4647
Many Fossil CGI scripts have this one property and no other.
48
+
4749
4850
<h2 id="directory">directory: <i>PATH</i></h2>
4951
5052
The PATH is the name of a directory that contains one or more Fossil
5153
repository files having the suffix ".fossil". If this property is used
@@ -52,23 +54,16 @@
5254
instead of [#repository|<b>repository:</b>], then the Fossil server is
5355
able to serve all of the repositories in the directory. The specific
5456
repository used is selected by a prefix on the PATH_INFO.
5557
5658
57
-<h2 id="errorlog">errorlog: <i>FILENAME</i></h2>
58
-
59
-This setting causes the server to log any errors in FILENAME.
60
-It is ok for multiple Fossil CGIs to share the same error log.
61
-
62
-Setting up an error log for Fossil servers is not required, but it
63
-is recommended.
64
-
6559
<h2 id="notfound">notfound: <i>URL</i></h2>
6660
6761
If the [#directory|<b>directory:</b>] option is used and if the PATH_INFO
6862
of the HTTP request does not correspond to any Fossil repository, then
6963
the request redirects to URL.
64
+
7065
7166
<h2 id="repolist">repolist</h2>
7267
7368
This is a Boolean property.
7469
If it is present, and if the [#directory:|<b>directory:</b>] option is used,
@@ -78,10 +73,75 @@
7873
The "skin" of the reply is determined by the first
7974
repository in the list that has a non-zero
8075
[/help?cmd=repolist-skin|repolist-skin] setting.
8176
If no repository has such a non-zero repolist-skin setting, then
8277
the repository list is generic HTML without any decoration.
78
+
79
+
80
+<h2 id="localauth">localauth</h2>
81
+
82
+This is a Boolean property.
83
+If it is present, [./caps/ref.html#s | setup capability]
84
+is granted to any HTTP request that
85
+comes in over a loopback interface, such as 127.0.0.1.
86
+
87
+
88
+<h2 id="skin">skin: <i>NAME</i></h2>
89
+
90
+If NAME is the name of one of the built-in skins supported by Fossil,
91
+then this option causes Fossil to display using that built-in skin,
92
+and to ignore any custom skin that might be configured in the repository
93
+itself.
94
+
95
+So, if you wanted to set up a server for a single Fossil project, but
96
+also give users the option to use several of the different built-in
97
+skins, you could create multiple CGI scripts, each with a different
98
+"<b>skin:</b>" property, but all pointing to the same <b>repository:</b>.
99
+Then users can select which skin to use by using the appropriate CGI.
100
+
101
+
102
+<h2 id="files">files: </i>GLOBLIST</i></h2>
103
+
104
+The GLOBLIST argument is a comma-separate list of "globs" that specify
105
+filenames. In [#directory|<b>directory:</b> mode], if the PATH_INFO
106
+does not identify any Fossil repository, but it does refer some other
107
+file in the directory, and that filename matches one of the glob patterns
108
+in the GLOBLIST, then the file is returned as static content.
109
+
110
+
111
+<h2 id="setenv">setenv: <i>NAME VALUE</i></h2>
112
+
113
+This parameter causes additional environment variable NAME to have VALUE.
114
+This parameter can be repeated as many times as necessary.
115
+
116
+
117
+<h2 id="HOME">HOME: <i>PATH</i></h2>
118
+
119
+This parameter is a short-hand for "<b>setenv HOME <i>PATH</i></b>".
120
+
121
+
122
+<h2 id="cgi-debug">cgi-debug: <i>FILE</i></h2>
123
+
124
+Cause CGI-related debugging information to be appended in <i>FILE</i>. Use
125
+this to help debug CGI problems.
126
+
127
+
128
+<h2 id="errorlog">errorlog: <i>FILENAME</i></h2>
129
+
130
+This setting causes the server to log any errors in FILENAME.
131
+It is ok for multiple Fossil CGIs to share the same error log.
132
+
133
+Setting up an error log for Fossil servers is not required, but it
134
+is recommended.
135
+
136
+
137
+<h2 id="timeout">timeout: <i>N</i></h2>
138
+
139
+This property changes the timeout on each CGI request to N seconds.
140
+If N is zero, then there is no timeout. If this property is omitted,
141
+then the default timeout is 300 seconds (5 minutes).
142
+
83143
84144
<h2 id="extroot">extroot: <i>PATH</i></h2>
85145
86146
This property defines the DOCUMENT_ROOT for the
87147
[./serverext.wiki|CGI Server Extensions]. If this property
@@ -93,52 +153,24 @@
93153
extension. The sub-CGI extension outputs reply text, when Fossil
94154
then (optionally) augments with its own header and footer and returns
95155
to the original requestor. The property controls the DOCUMENT_ROOT
96156
of the sub-CGI.
97157
98
-<h2 id="timeout">timeout: <i>N</i></h2>
99
-
100
-This property changes the timeout on each CGI request to N seconds.
101
-If N is zero, then there is no timeout. If this property is omitted,
102
-then the default timeout is 300 seconds (5 minutes).
103
-
104
-<h2 id="localauth">localauth</h2>
105
-
106
-This is a Boolean property.
107
-If it is present, [./caps/ref.html#s | setup capability]
108
-is granted to any HTTP request that
109
-comes in over a loopback interface, such as 127.0.0.1.
110
-
111
-<h2 id="skin">skin: <i>NAME</i></h2>
112
-
113
-If NAME is the name of one of the built-in skins supported by Fossil,
114
-then this option causes Fossil to display using that built-in skin,
115
-and to ignore any custom skin that might be configured in the repository
116
-itself.
117
-
118
-So, if you wanted to set up a server for a single Fossil project, but
119
-also give users the option to use several of the different built-in
120
-skins, you could create multiple CGI scripts, each with a different
121
-"<b>skin:</b>" property, but all pointing to the same <b>repository:</b>.
122
-Then users can select which skin to use by using the appropriate CGI.
123
-
124
-<h2 id="files">files: </i>GLOBLIST</i></h2>
125
-
126
-The GLOBLIST argument is a comma-separate list of "globs" that specify
127
-filenames. In [#directory|<b>directory:</b> mode], if the PATH_INFO
128
-does not identify any Fossil repository, but it does refer some other
129
-file in the directory, and that filename matches one of the glob patterns
130
-in the GLOBLIST, then the file is returned as static content.
131
-
132
-<h2 id="setenv">setenv: <i>NAME VALUE</i></h2>
133
-
134
-This parameter causes additional environment variable NAME to have VALUE.
135
-This parameter can be repeated as many times as necessary.
136
-
137
-<h2 id="HOME">HOME: <i>PATH</i></h2>
138
-
139
-This parameter is a short-hand for "<b>setenv HOME <i>PATH</i></b>".
158
+
159
+<h2 id="redirect">redirect: <i>REPO URL</i></h2>
160
+
161
+Extract the "name" query parameter and search REPO for a check-in or
162
+ticket that matches the value of "name", then redirect to URL. There
163
+can be multiple "redirect:" lines that are processed in order. If the
164
+repo name is "*", then an unconditional redirect to URL is taken.
165
+
166
+
167
+<h2 id="jsmode">jsmode: <i>VALUE</i></h2>
168
+
169
+Specifies the delivery mode for JavaScript files. See "[/help?cmd=http |
170
+http --jsmode]" for the allowed values and their meanings.
171
+
140172
141173
<h2 id="mainmenu">mainmenu: <i>FILE</i></h2>
142174
143175
This parameter causes the contents of the given file to override the
144176
site's <tt>mainmenu</tt> configuration setting, much in the same way
@@ -145,11 +177,5 @@
145177
that the <tt>skin</tt> setting overrides the skin. This can be used to
146178
apply a common main menu to a number of sites, and centrally maintain
147179
it, without having to copy its contents into each site. Note, however,
148180
that the contents of this setting are not stored in the repository and
149181
will not be cloned along with the repository.
150
-
151
-
152
-<h2 id="cgi-debug">cgi-debug: <i>FILE</i></h2>
153
-
154
-Cause CGI-related debugging information to be appended in <i>FILE</i>. Use
155
-this to help debug CGI problems.
156182
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -36,16 +36,18 @@
36 the form "<b>property:&nbsp;argument&nbsp;...</b>".
37 The remainder of this document describes the available properties and
38 their arguments.
39
40 <hr>
 
41 <h2 id="repository">repository: <i>PATH</i></h2>
42
43 This property defines the Fossil repository that the server will use.
44 Every Fossil CGI requires either this property or the
45 [#directory|<b>directory:</b>] property (but not both).
46 Many Fossil CGI scripts have this one property and no other.
 
47
48 <h2 id="directory">directory: <i>PATH</i></h2>
49
50 The PATH is the name of a directory that contains one or more Fossil
51 repository files having the suffix ".fossil". If this property is used
@@ -52,23 +54,16 @@
52 instead of [#repository|<b>repository:</b>], then the Fossil server is
53 able to serve all of the repositories in the directory. The specific
54 repository used is selected by a prefix on the PATH_INFO.
55
56
57 <h2 id="errorlog">errorlog: <i>FILENAME</i></h2>
58
59 This setting causes the server to log any errors in FILENAME.
60 It is ok for multiple Fossil CGIs to share the same error log.
61
62 Setting up an error log for Fossil servers is not required, but it
63 is recommended.
64
65 <h2 id="notfound">notfound: <i>URL</i></h2>
66
67 If the [#directory|<b>directory:</b>] option is used and if the PATH_INFO
68 of the HTTP request does not correspond to any Fossil repository, then
69 the request redirects to URL.
 
70
71 <h2 id="repolist">repolist</h2>
72
73 This is a Boolean property.
74 If it is present, and if the [#directory:|<b>directory:</b>] option is used,
@@ -78,10 +73,75 @@
78 The "skin" of the reply is determined by the first
79 repository in the list that has a non-zero
80 [/help?cmd=repolist-skin|repolist-skin] setting.
81 If no repository has such a non-zero repolist-skin setting, then
82 the repository list is generic HTML without any decoration.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
84 <h2 id="extroot">extroot: <i>PATH</i></h2>
85
86 This property defines the DOCUMENT_ROOT for the
87 [./serverext.wiki|CGI Server Extensions]. If this property
@@ -93,52 +153,24 @@
93 extension. The sub-CGI extension outputs reply text, when Fossil
94 then (optionally) augments with its own header and footer and returns
95 to the original requestor. The property controls the DOCUMENT_ROOT
96 of the sub-CGI.
97
98 <h2 id="timeout">timeout: <i>N</i></h2>
99
100 This property changes the timeout on each CGI request to N seconds.
101 If N is zero, then there is no timeout. If this property is omitted,
102 then the default timeout is 300 seconds (5 minutes).
103
104 <h2 id="localauth">localauth</h2>
105
106 This is a Boolean property.
107 If it is present, [./caps/ref.html#s | setup capability]
108 is granted to any HTTP request that
109 comes in over a loopback interface, such as 127.0.0.1.
110
111 <h2 id="skin">skin: <i>NAME</i></h2>
112
113 If NAME is the name of one of the built-in skins supported by Fossil,
114 then this option causes Fossil to display using that built-in skin,
115 and to ignore any custom skin that might be configured in the repository
116 itself.
117
118 So, if you wanted to set up a server for a single Fossil project, but
119 also give users the option to use several of the different built-in
120 skins, you could create multiple CGI scripts, each with a different
121 "<b>skin:</b>" property, but all pointing to the same <b>repository:</b>.
122 Then users can select which skin to use by using the appropriate CGI.
123
124 <h2 id="files">files: </i>GLOBLIST</i></h2>
125
126 The GLOBLIST argument is a comma-separate list of "globs" that specify
127 filenames. In [#directory|<b>directory:</b> mode], if the PATH_INFO
128 does not identify any Fossil repository, but it does refer some other
129 file in the directory, and that filename matches one of the glob patterns
130 in the GLOBLIST, then the file is returned as static content.
131
132 <h2 id="setenv">setenv: <i>NAME VALUE</i></h2>
133
134 This parameter causes additional environment variable NAME to have VALUE.
135 This parameter can be repeated as many times as necessary.
136
137 <h2 id="HOME">HOME: <i>PATH</i></h2>
138
139 This parameter is a short-hand for "<b>setenv HOME <i>PATH</i></b>".
140
141 <h2 id="mainmenu">mainmenu: <i>FILE</i></h2>
142
143 This parameter causes the contents of the given file to override the
144 site's <tt>mainmenu</tt> configuration setting, much in the same way
@@ -145,11 +177,5 @@
145 that the <tt>skin</tt> setting overrides the skin. This can be used to
146 apply a common main menu to a number of sites, and centrally maintain
147 it, without having to copy its contents into each site. Note, however,
148 that the contents of this setting are not stored in the repository and
149 will not be cloned along with the repository.
150
151
152 <h2 id="cgi-debug">cgi-debug: <i>FILE</i></h2>
153
154 Cause CGI-related debugging information to be appended in <i>FILE</i>. Use
155 this to help debug CGI problems.
156
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -36,16 +36,18 @@
36 the form "<b>property:&nbsp;argument&nbsp;...</b>".
37 The remainder of this document describes the available properties and
38 their arguments.
39
40 <hr>
41
42 <h2 id="repository">repository: <i>PATH</i></h2>
43
44 This property defines the Fossil repository that the server will use.
45 Every Fossil CGI requires either this property or the
46 [#directory|<b>directory:</b>] property (but not both).
47 Many Fossil CGI scripts have this one property and no other.
48
49
50 <h2 id="directory">directory: <i>PATH</i></h2>
51
52 The PATH is the name of a directory that contains one or more Fossil
53 repository files having the suffix ".fossil". If this property is used
@@ -52,23 +54,16 @@
54 instead of [#repository|<b>repository:</b>], then the Fossil server is
55 able to serve all of the repositories in the directory. The specific
56 repository used is selected by a prefix on the PATH_INFO.
57
58
 
 
 
 
 
 
 
 
59 <h2 id="notfound">notfound: <i>URL</i></h2>
60
61 If the [#directory|<b>directory:</b>] option is used and if the PATH_INFO
62 of the HTTP request does not correspond to any Fossil repository, then
63 the request redirects to URL.
64
65
66 <h2 id="repolist">repolist</h2>
67
68 This is a Boolean property.
69 If it is present, and if the [#directory:|<b>directory:</b>] option is used,
@@ -78,10 +73,75 @@
73 The "skin" of the reply is determined by the first
74 repository in the list that has a non-zero
75 [/help?cmd=repolist-skin|repolist-skin] setting.
76 If no repository has such a non-zero repolist-skin setting, then
77 the repository list is generic HTML without any decoration.
78
79
80 <h2 id="localauth">localauth</h2>
81
82 This is a Boolean property.
83 If it is present, [./caps/ref.html#s | setup capability]
84 is granted to any HTTP request that
85 comes in over a loopback interface, such as 127.0.0.1.
86
87
88 <h2 id="skin">skin: <i>NAME</i></h2>
89
90 If NAME is the name of one of the built-in skins supported by Fossil,
91 then this option causes Fossil to display using that built-in skin,
92 and to ignore any custom skin that might be configured in the repository
93 itself.
94
95 So, if you wanted to set up a server for a single Fossil project, but
96 also give users the option to use several of the different built-in
97 skins, you could create multiple CGI scripts, each with a different
98 "<b>skin:</b>" property, but all pointing to the same <b>repository:</b>.
99 Then users can select which skin to use by using the appropriate CGI.
100
101
102 <h2 id="files">files: </i>GLOBLIST</i></h2>
103
104 The GLOBLIST argument is a comma-separate list of "globs" that specify
105 filenames. In [#directory|<b>directory:</b> mode], if the PATH_INFO
106 does not identify any Fossil repository, but it does refer some other
107 file in the directory, and that filename matches one of the glob patterns
108 in the GLOBLIST, then the file is returned as static content.
109
110
111 <h2 id="setenv">setenv: <i>NAME VALUE</i></h2>
112
113 This parameter causes additional environment variable NAME to have VALUE.
114 This parameter can be repeated as many times as necessary.
115
116
117 <h2 id="HOME">HOME: <i>PATH</i></h2>
118
119 This parameter is a short-hand for "<b>setenv HOME <i>PATH</i></b>".
120
121
122 <h2 id="cgi-debug">cgi-debug: <i>FILE</i></h2>
123
124 Cause CGI-related debugging information to be appended in <i>FILE</i>. Use
125 this to help debug CGI problems.
126
127
128 <h2 id="errorlog">errorlog: <i>FILENAME</i></h2>
129
130 This setting causes the server to log any errors in FILENAME.
131 It is ok for multiple Fossil CGIs to share the same error log.
132
133 Setting up an error log for Fossil servers is not required, but it
134 is recommended.
135
136
137 <h2 id="timeout">timeout: <i>N</i></h2>
138
139 This property changes the timeout on each CGI request to N seconds.
140 If N is zero, then there is no timeout. If this property is omitted,
141 then the default timeout is 300 seconds (5 minutes).
142
143
144 <h2 id="extroot">extroot: <i>PATH</i></h2>
145
146 This property defines the DOCUMENT_ROOT for the
147 [./serverext.wiki|CGI Server Extensions]. If this property
@@ -93,52 +153,24 @@
153 extension. The sub-CGI extension outputs reply text, when Fossil
154 then (optionally) augments with its own header and footer and returns
155 to the original requestor. The property controls the DOCUMENT_ROOT
156 of the sub-CGI.
157
158
159 <h2 id="redirect">redirect: <i>REPO URL</i></h2>
160
161 Extract the "name" query parameter and search REPO for a check-in or
162 ticket that matches the value of "name", then redirect to URL. There
163 can be multiple "redirect:" lines that are processed in order. If the
164 repo name is "*", then an unconditional redirect to URL is taken.
165
166
167 <h2 id="jsmode">jsmode: <i>VALUE</i></h2>
168
169 Specifies the delivery mode for JavaScript files. See "[/help?cmd=http |
170 http --jsmode]" for the allowed values and their meanings.
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
173 <h2 id="mainmenu">mainmenu: <i>FILE</i></h2>
174
175 This parameter causes the contents of the given file to override the
176 site's <tt>mainmenu</tt> configuration setting, much in the same way
@@ -145,11 +177,5 @@
177 that the <tt>skin</tt> setting overrides the skin. This can be used to
178 apply a common main menu to a number of sites, and centrally maintain
179 it, without having to copy its contents into each site. Note, however,
180 that the contents of this setting are not stored in the repository and
181 will not be cloned along with the repository.
 
 
 
 
 
 
182
+47 -13
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,14 +1,28 @@
11
<title>Change Log</title>
22
33
<a name='v2_15'></a>
44
<h2>Changes for Version 2.15 (pending)</h2>
5
- * The built-in skins all use the "mainmenu" setting to determine
6
- the content of the main menu. The ability to edit the
5
+ * The [./defcsp.md|default CSP] has been relaxed slightly to allow
6
+ images to be loaded from any URL. All other resources are still
7
+ locked down by default.
8
+ * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
9
+ setting to determine the content of the main menu.
10
+ The ability to edit the
711
"mainmenu" setting is added on the /Admin/Configuration page.
12
+ * The hamburger menu is now available on most of the built-in skins.
13
+ * Any built-in skin named "X" can be used instead of the standard
14
+ repository skin by adding the URL parameter <tt>skin=X</tt> to the
15
+ request. The selection is persisted using the display
16
+ preferences cookie unless the "once" query parameter is also
17
+ included. The [/skins] page may be used to select a skin.
18
+ * The [/cookies] page now gives the user an opportunity to delete
19
+ individual cookies. And the /cookies page is linked from the
20
+ /sitemap, so that it appears in hamburger menus.
821
* The [/sitemap] extensions are now specified by a single new
9
- setting "sitemap-extra", rather than a cluster of various
22
+ "[/help?cmd=sitemap-extra|sitemap-extra setting]",
23
+ rather than a cluster of various
1024
"sitemap-*" settings. The older settings are no longer used.
1125
<b>This change might require minor server configuration
1226
adjustments on servers that use /sitemap extensions.</b>
1327
The /Admin/Configuration page provides the ability to edit
1428
the new "sitemap-extra" setting.
@@ -17,30 +31,50 @@
1731
[/help?cmd=http|fossil http]. This option causes Fossil to
1832
understand URIs of the form "/doc/NAME/..." as if they were
1933
"[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
2034
[./embeddeddoc.wiki|embedded documentation] changes prior to
2135
check-in.
36
+ * For diff web pages, if the diff type (unified versus side-by-side)
37
+ is not specified by a query parameter, and if the
38
+ "[/help?cmd=preferred-diff-type|preferred-diff-type]"
39
+ setting is omitted or less than 1, then select the diff type based
40
+ on a guess of whether or not the request is coming from a mobile
41
+ device. Mobile gets unified and desktop gets side-by-side.
42
+ * The various pages which show diffs now have toggles to show/hide
43
+ individual diffs.
44
+ * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]"
45
+ setting to allow an admin to force a default diff type.
2246
* The "pikchr-background" settings is now available in
2347
"detail.txt" skin files, for better control of Pikchr
2448
colors in inverted color schemes.
25
- * The hamburger menu is now available on the built-in
26
- "[/skn_ardoise/doc/trunk/www/changes.wiki#v2_15|ardoise]" and
27
- "[/skn_plain_gray/doc/trunk/www/changes.wiki#v2_15|plain_gray]"
28
- skins.
29
- * Add the --list option to the
49
+ * Add the <tt>--list</tt> option to the
3050
[/help?cmd=tarball|tarball],
3151
[/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
3252
commands.
3353
* The javascript used to implement the hamburger menu on the
3454
default built-in skin has been made generic so that it is usable
3555
by a variety of skins, and promoted to an ordinary built-in
3656
javascript file.
37
- * Any built-in skin named "X" can be used instead of the standard
38
- repository skin by including the term "skn_X" at the beginning
39
- of the URL path.
40
- * New TH1 commands: "builtin_request_js", "capexpr",
41
- "foreach", "string match"
57
+ * New TH1 commands:
58
+ "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
59
+ "[/doc/trunk/www/th1.md#capexpr|capexpr]",
60
+ "foreach", "lappend", and "string match"
61
+ * The [/help/leaves|leaves command] now shows the branch point
62
+ of each leaf.
63
+ * The [/help?cmd=add|fossil add] command refuses to add files whose
64
+ names are reserved by Windows (ex: "aux") unless the --allow-reserved
65
+ option is included. This helps prevent unix users from accidentally
66
+ creating check-ins that are unreadable by Windows users.
67
+ * Add the "re=" query parameter to the [/help?cmd=/dir|/dir] webpage,
68
+ for symetry with the [/help?cmd=/tree|/tree] page.
69
+ * Update the built-in SQLite to version 3.35.0.
70
+ * The ./configure script now has the --print-minimum-sqlite-version option
71
+ that prints the minimum SQLite version required by the current version
72
+ of Fossil. This might be used by integrators who insist on building
73
+ Fossil to link against the system SQLite library rather than the
74
+ built-in copy of SQLite, to verify that their system SQLite library
75
+ is recent enough.
4276
4377
<a name='v2_14'></a>
4478
<h2>Changes for Version 2.14 (2021-01-20)</h2>
4579
4680
* <b>Schema Update Notice #1:</b>
4781
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,14 +1,28 @@
1 <title>Change Log</title>
2
3 <a name='v2_15'></a>
4 <h2>Changes for Version 2.15 (pending)</h2>
5 * The built-in skins all use the "mainmenu" setting to determine
6 the content of the main menu. The ability to edit the
 
 
 
 
7 "mainmenu" setting is added on the /Admin/Configuration page.
 
 
 
 
 
 
 
 
 
8 * The [/sitemap] extensions are now specified by a single new
9 setting "sitemap-extra", rather than a cluster of various
 
10 "sitemap-*" settings. The older settings are no longer used.
11 <b>This change might require minor server configuration
12 adjustments on servers that use /sitemap extensions.</b>
13 The /Admin/Configuration page provides the ability to edit
14 the new "sitemap-extra" setting.
@@ -17,30 +31,50 @@
17 [/help?cmd=http|fossil http]. This option causes Fossil to
18 understand URIs of the form "/doc/NAME/..." as if they were
19 "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
20 [./embeddeddoc.wiki|embedded documentation] changes prior to
21 check-in.
 
 
 
 
 
 
 
 
 
 
22 * The "pikchr-background" settings is now available in
23 "detail.txt" skin files, for better control of Pikchr
24 colors in inverted color schemes.
25 * The hamburger menu is now available on the built-in
26 "[/skn_ardoise/doc/trunk/www/changes.wiki#v2_15|ardoise]" and
27 "[/skn_plain_gray/doc/trunk/www/changes.wiki#v2_15|plain_gray]"
28 skins.
29 * Add the --list option to the
30 [/help?cmd=tarball|tarball],
31 [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
32 commands.
33 * The javascript used to implement the hamburger menu on the
34 default built-in skin has been made generic so that it is usable
35 by a variety of skins, and promoted to an ordinary built-in
36 javascript file.
37 * Any built-in skin named "X" can be used instead of the standard
38 repository skin by including the term "skn_X" at the beginning
39 of the URL path.
40 * New TH1 commands: "builtin_request_js", "capexpr",
41 "foreach", "string match"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
43 <a name='v2_14'></a>
44 <h2>Changes for Version 2.14 (2021-01-20)</h2>
45
46 * <b>Schema Update Notice #1:</b>
47
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,14 +1,28 @@
1 <title>Change Log</title>
2
3 <a name='v2_15'></a>
4 <h2>Changes for Version 2.15 (pending)</h2>
5 * The [./defcsp.md|default CSP] has been relaxed slightly to allow
6 images to be loaded from any URL. All other resources are still
7 locked down by default.
8 * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
9 setting to determine the content of the main menu.
10 The ability to edit the
11 "mainmenu" setting is added on the /Admin/Configuration page.
12 * The hamburger menu is now available on most of the built-in skins.
13 * Any built-in skin named "X" can be used instead of the standard
14 repository skin by adding the URL parameter <tt>skin=X</tt> to the
15 request. The selection is persisted using the display
16 preferences cookie unless the "once" query parameter is also
17 included. The [/skins] page may be used to select a skin.
18 * The [/cookies] page now gives the user an opportunity to delete
19 individual cookies. And the /cookies page is linked from the
20 /sitemap, so that it appears in hamburger menus.
21 * The [/sitemap] extensions are now specified by a single new
22 "[/help?cmd=sitemap-extra|sitemap-extra setting]",
23 rather than a cluster of various
24 "sitemap-*" settings. The older settings are no longer used.
25 <b>This change might require minor server configuration
26 adjustments on servers that use /sitemap extensions.</b>
27 The /Admin/Configuration page provides the ability to edit
28 the new "sitemap-extra" setting.
@@ -17,30 +31,50 @@
31 [/help?cmd=http|fossil http]. This option causes Fossil to
32 understand URIs of the form "/doc/NAME/..." as if they were
33 "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
34 [./embeddeddoc.wiki|embedded documentation] changes prior to
35 check-in.
36 * For diff web pages, if the diff type (unified versus side-by-side)
37 is not specified by a query parameter, and if the
38 "[/help?cmd=preferred-diff-type|preferred-diff-type]"
39 setting is omitted or less than 1, then select the diff type based
40 on a guess of whether or not the request is coming from a mobile
41 device. Mobile gets unified and desktop gets side-by-side.
42 * The various pages which show diffs now have toggles to show/hide
43 individual diffs.
44 * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]"
45 setting to allow an admin to force a default diff type.
46 * The "pikchr-background" settings is now available in
47 "detail.txt" skin files, for better control of Pikchr
48 colors in inverted color schemes.
49 * Add the <tt>--list</tt> option to the
 
 
 
 
50 [/help?cmd=tarball|tarball],
51 [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
52 commands.
53 * The javascript used to implement the hamburger menu on the
54 default built-in skin has been made generic so that it is usable
55 by a variety of skins, and promoted to an ordinary built-in
56 javascript file.
57 * New TH1 commands:
58 "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
59 "[/doc/trunk/www/th1.md#capexpr|capexpr]",
60 "foreach", "lappend", and "string match"
61 * The [/help/leaves|leaves command] now shows the branch point
62 of each leaf.
63 * The [/help?cmd=add|fossil add] command refuses to add files whose
64 names are reserved by Windows (ex: "aux") unless the --allow-reserved
65 option is included. This helps prevent unix users from accidentally
66 creating check-ins that are unreadable by Windows users.
67 * Add the "re=" query parameter to the [/help?cmd=/dir|/dir] webpage,
68 for symetry with the [/help?cmd=/tree|/tree] page.
69 * Update the built-in SQLite to version 3.35.0.
70 * The ./configure script now has the --print-minimum-sqlite-version option
71 that prints the minimum SQLite version required by the current version
72 of Fossil. This might be used by integrators who insist on building
73 Fossil to link against the system SQLite library rather than the
74 built-in copy of SQLite, to verify that their system SQLite library
75 is recent enough.
76
77 <a name='v2_14'></a>
78 <h2>Changes for Version 2.14 (2021-01-20)</h2>
79
80 * <b>Schema Update Notice #1:</b>
81
+5 -4
--- www/chat.md
+++ www/chat.md
@@ -94,14 +94,15 @@
9494
### Deletion of Messages
9595
9696
Any user may *locally* delete a given message by clicking on the "tab"
9797
at the top of the message and clicking the button which appears. Such
9898
deletions are local-only, and the messages will reappear if the page
99
-is reloaded. Admin users may additionally choose to globally
100
-delete a message from the chat record, which deletes it not only from
101
-their own browser but also propagates the removal to all connected
102
-clients the next time they poll for new messages.
99
+is reloaded. The user who posted a given message, or any Admin users,
100
+may additionally choose to globally delete a message from the chat
101
+record, which deletes it not only from their own browser but also
102
+propagates the removal to all connected clients the next time they
103
+poll for new messages.
103104
104105
## Implementation Details
105106
106107
*You do not need to understand how Fossil chat works in order to use it.
107108
But many developers prefer to know how their tools work.
108109
--- www/chat.md
+++ www/chat.md
@@ -94,14 +94,15 @@
94 ### Deletion of Messages
95
96 Any user may *locally* delete a given message by clicking on the "tab"
97 at the top of the message and clicking the button which appears. Such
98 deletions are local-only, and the messages will reappear if the page
99 is reloaded. Admin users may additionally choose to globally
100 delete a message from the chat record, which deletes it not only from
101 their own browser but also propagates the removal to all connected
102 clients the next time they poll for new messages.
 
103
104 ## Implementation Details
105
106 *You do not need to understand how Fossil chat works in order to use it.
107 But many developers prefer to know how their tools work.
108
--- www/chat.md
+++ www/chat.md
@@ -94,14 +94,15 @@
94 ### Deletion of Messages
95
96 Any user may *locally* delete a given message by clicking on the "tab"
97 at the top of the message and clicking the button which appears. Such
98 deletions are local-only, and the messages will reappear if the page
99 is reloaded. The user who posted a given message, or any Admin users,
100 may additionally choose to globally delete a message from the chat
101 record, which deletes it not only from their own browser but also
102 propagates the removal to all connected clients the next time they
103 poll for new messages.
104
105 ## Implementation Details
106
107 *You do not need to understand how Fossil chat works in order to use it.
108 But many developers prefer to know how their tools work.
109
--- www/customskin.md
+++ www/customskin.md
@@ -5,11 +5,11 @@
55
the look and feel (the "skin") of Fossil to better suite your own individual tastes.
66
This document provides background information to aid you in that task.
77
88
## <a name="builtin"></a>Built-in Skins
99
10
-Fossil comes with multiple built-in skins. If the default skin does not
10
+Fossil comes with [multiple built-in skins](/skins). If the default skin does not
1111
suite your tastes, perhaps one of the other built-in skins will work better.
1212
If nothing else, the built-in skins can serve as examples or templates that
1313
you can use to develop your own custom skin.
1414
1515
The sources to these built-ins can
@@ -134,10 +134,27 @@
134134
Finally, Fossil always adds its own footer (unless overridden)
135135
to close out the generated HTML:
136136
137137
</body>
138138
</html>
139
+
140
+## <a name="mainmenu"></a>Changing the Main Menu Contents
141
+
142
+As of Fossil 2.15, the actual text content of the skin’s main menu is no
143
+longer part of the skin proper if you’re using one of the stock skins.
144
+If you look at the Header section of the skin, you’ll find a
145
+`<div class="mainmenu">` element whose contents are set by a short
146
+[TH1](./th1.md) script from the contents of the **Main Menu** section of
147
+the Setup → Configuration screen.
148
+
149
+This feature allows the main menu contents to stay the same across
150
+different skins, so you no longer have to reapply menu customizations
151
+when trying different skins.
152
+
153
+See the [`capexpr`](./th1.md#capexpr) section of the TH1 docs for help
154
+on interpreting the default contents of this block.
155
+
139156
140157
## <a name="override"></a>Overriding the HTML Header and Footer
141158
142159
Notice that the `<html>`, `<head>`, and opening `<body>`
143160
elements at the beginning of the document,
144161
--- www/customskin.md
+++ www/customskin.md
@@ -5,11 +5,11 @@
5 the look and feel (the "skin") of Fossil to better suite your own individual tastes.
6 This document provides background information to aid you in that task.
7
8 ## <a name="builtin"></a>Built-in Skins
9
10 Fossil comes with multiple built-in skins. If the default skin does not
11 suite your tastes, perhaps one of the other built-in skins will work better.
12 If nothing else, the built-in skins can serve as examples or templates that
13 you can use to develop your own custom skin.
14
15 The sources to these built-ins can
@@ -134,10 +134,27 @@
134 Finally, Fossil always adds its own footer (unless overridden)
135 to close out the generated HTML:
136
137 </body>
138 </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
140 ## <a name="override"></a>Overriding the HTML Header and Footer
141
142 Notice that the `<html>`, `<head>`, and opening `<body>`
143 elements at the beginning of the document,
144
--- www/customskin.md
+++ www/customskin.md
@@ -5,11 +5,11 @@
5 the look and feel (the "skin") of Fossil to better suite your own individual tastes.
6 This document provides background information to aid you in that task.
7
8 ## <a name="builtin"></a>Built-in Skins
9
10 Fossil comes with [multiple built-in skins](/skins). If the default skin does not
11 suite your tastes, perhaps one of the other built-in skins will work better.
12 If nothing else, the built-in skins can serve as examples or templates that
13 you can use to develop your own custom skin.
14
15 The sources to these built-ins can
@@ -134,10 +134,27 @@
134 Finally, Fossil always adds its own footer (unless overridden)
135 to close out the generated HTML:
136
137 </body>
138 </html>
139
140 ## <a name="mainmenu"></a>Changing the Main Menu Contents
141
142 As of Fossil 2.15, the actual text content of the skin’s main menu is no
143 longer part of the skin proper if you’re using one of the stock skins.
144 If you look at the Header section of the skin, you’ll find a
145 `<div class="mainmenu">` element whose contents are set by a short
146 [TH1](./th1.md) script from the contents of the **Main Menu** section of
147 the Setup → Configuration screen.
148
149 This feature allows the main menu contents to stay the same across
150 different skins, so you no longer have to reapply menu customizations
151 when trying different skins.
152
153 See the [`capexpr`](./th1.md#capexpr) section of the TH1 docs for help
154 on interpreting the default contents of this block.
155
156
157 ## <a name="override"></a>Overriding the HTML Header and Footer
158
159 Notice that the `<html>`, `<head>`, and opening `<body>`
160 elements at the beginning of the document,
161
+50 -46
--- www/defcsp.md
+++ www/defcsp.md
@@ -1,58 +1,48 @@
11
# The Default Content Security Policy (CSP)
22
33
When Fossil’s web interface generates an HTML page, it normally includes
4
-a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP defines
5
-a “white list” to tell the browser what types of content (HTML, images,
6
-CSS, JavaScript...) the document may reference and the sources the
7
-browser is allowed to pull and interpret such content from. The aim is to prevent
8
-certain classes of [cross-site scripting][xss] (XSS) and code injection
9
-attacks. The browser will not pull content types disallowed by the CSP;
10
-the CSP also adds restrictions on the types of inline content the
11
-browser is allowed to interpret.
12
-
13
-Fossil has built-in server-side content filtering logic. For example, it
14
-purposely breaks `<script>` tags when it finds them in Markdown and
15
-Fossil Wiki documents. (But not in [HTML-formatted embedded
16
-docs][hfed]!) We also back that with multiple levels of analysis and
17
-checks to find and fix content security problems: compile-time static
18
-analysis, run-time dynamic analysis, and manual code inspection. Fossil
19
-is open source software, so it benefits from the “[many
20
-eyeballs][llaw],” limited by the size of its developer community.
21
-
22
-However, there is a practical limit to the power of server-side
23
-filtering and code quality practices.
24
-
25
-First, there is an endless battle between those looking for clever paths
26
-around such barriers and those erecting the barriers. The developers of
27
-Fossil are committed to holding up our end of that fight, but this is,
28
-to some extent, a reactive posture. It is cold comfort if Fossil’s
29
-developers react quickly to a report of code injection — as we do! — if
30
-the bad guys learn of it and start exploiting it first.
31
-
32
-Second, Fossil has purposefully powerful features that are inherently
33
-difficult to police from the server side: HTML tags [in wiki](/wiki_rules)
34
-and [in Markdown](/md_rules) docs, [TH1 docs](./th1.md), the Admin →
35
-Wiki → “Use HTML as wiki markup language” mode, etc.
36
-
37
-Fossil’s strong default CSP adds client-side filtering as a backstop for
38
-all of this.
39
-
40
-Fossil site administrators can [modify the default CSP](#override), perhaps
41
-to add trusted external sources for auxiliary content. But for maximum
42
-safety, site developers are encouraged to work within the restrictions
43
-imposed by the default CSP and avoid the temptation to relax the CSP
44
-unless they fully understand the security implications of what they are
45
-doing.
46
-
47
-[llaw]: https://en.wikipedia.org/wiki/Linus%27s_Law
48
-
4
+a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP specifies
5
+allowed sources for external resources such as images,
6
+CSS, javascript, and so froth.
7
+The purpose of CSP is to provide an extra layer of protection against
8
+[cross-site scripting][xss] (XSS) and code injection
9
+attacks. Compatible web browsers will not use external resources unless
10
+they are specifically allowed by the CSP, which dramatically reduces
11
+the attack surface of the application.
12
+
13
+Fossil does not rely on CSP for security.
14
+A Fossil server should be secure from attack even with out CSP.
15
+Fossil includes built-in server-side content filtering logic.
16
+For example, Fossil purposely breaks `<script>` tags when it finds
17
+them in Markdown and Fossil Wiki documents. And the Fossil build
18
+process scans the source code for potential injection vulnerabilities
19
+and refuses to compile if any problems are found.
20
+However, CSP provides an additional layer of defense against undetected
21
+bugs that might lead to a vulnerability.
4922
5023
## The Default Restrictions
5124
52
-The Fossil default CSP declares the following content restrictions:
25
+The default CSP used by Fossil is as follows:
5326
27
+<pre>
28
+ default-src 'self' data:;
29
+ script-src 'self' 'nonce-$nonce';
30
+ style-src 'self' 'unsafe-inline';
31
+ img-src * data:;
32
+</pre>
33
+
34
+The default is recommended for most installations. However,
35
+the site administrators can overwrite this default DSP using the
36
+[default-csp setting](/help?cmd=default-csp). For example,
37
+CSP restrictions can be completely disabled by setting the default-csp to:
38
+
39
+<pre>
40
+ default-src *;
41
+</pre>
42
+
43
+The following sections detail the maining of the default CSP setting.
5444
5545
### <a name="base"></a> default-src 'self' data:
5646
5747
This policy means mixed-origin content isn’t allowed, so you can’t refer
5848
to resources on other web domains. Browsers will ignore a link like the
@@ -86,10 +76,23 @@
8676
There are many other cases, [covered below](#serving).
8777
8878
[b64]: https://en.wikipedia.org/wiki/Base64
8979
[svr]: ./server/
9080
81
+
82
+### <a name="img"></a> img-src * data:
83
+
84
+As of Fossil 2.15, we don’t restrict the source of inline images at all.
85
+You can pull them in from remote systems as well as pull them from
86
+within the Fossil repository itself, or use `data:` URIs.
87
+
88
+If you are certain all images come from only within the repository, you
89
+can close off certain risks — tracking pixels, broken image format
90
+decoders, system dialog box spoofing, etc. — by changing this to
91
+“`img-src 'self'`” possibly followed by “`data:`” if you will also use
92
+`data:` URIs.
93
+
9194
9295
### <a name="style"></a> style-src 'self' 'unsafe-inline'
9396
9497
This policy allows CSS information to come from separate files hosted
9598
under the Fossil repo server’s Internet domain. It also allows inline CSS
@@ -106,10 +109,11 @@
106109
flexibility and the work-arounds are verbose and difficult to maintain.
107110
Furthermore, the harm that can be done with style injections is far
108111
less than the harm possible with injected javascript. And so the
109112
`'unsafe-inline'` compromise is accepted for now, though it might
110113
go away in some future release of Fossil.
114
+
111115
112116
### <a name="script"></a> script-src 'self' 'nonce-%s'
113117
114118
This policy disables in-line JavaScript and only allows `<script>`
115119
elements if the `<script>` includes a `nonce` attribute that matches the
116120
117121
ADDED www/delta-manifests.md
--- www/defcsp.md
+++ www/defcsp.md
@@ -1,58 +1,48 @@
1 # The Default Content Security Policy (CSP)
2
3 When Fossil’s web interface generates an HTML page, it normally includes
4 a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP defines
5 a “white list” to tell the browser what types of content (HTML, images,
6 CSS, JavaScript...) the document may reference and the sources the
7 browser is allowed to pull and interpret such content from. The aim is to prevent
8 certain classes of [cross-site scripting][xss] (XSS) and code injection
9 attacks. The browser will not pull content types disallowed by the CSP;
10 the CSP also adds restrictions on the types of inline content the
11 browser is allowed to interpret.
12
13 Fossil has built-in server-side content filtering logic. For example, it
14 purposely breaks `<script>` tags when it finds them in Markdown and
15 Fossil Wiki documents. (But not in [HTML-formatted embedded
16 docs][hfed]!) We also back that with multiple levels of analysis and
17 checks to find and fix content security problems: compile-time static
18 analysis, run-time dynamic analysis, and manual code inspection. Fossil
19 is open source software, so it benefits from the “[many
20 eyeballs][llaw],” limited by the size of its developer community.
21
22 However, there is a practical limit to the power of server-side
23 filtering and code quality practices.
24
25 First, there is an endless battle between those looking for clever paths
26 around such barriers and those erecting the barriers. The developers of
27 Fossil are committed to holding up our end of that fight, but this is,
28 to some extent, a reactive posture. It is cold comfort if Fossil’s
29 developers react quickly to a report of code injection — as we do! — if
30 the bad guys learn of it and start exploiting it first.
31
32 Second, Fossil has purposefully powerful features that are inherently
33 difficult to police from the server side: HTML tags [in wiki](/wiki_rules)
34 and [in Markdown](/md_rules) docs, [TH1 docs](./th1.md), the Admin →
35 Wiki → “Use HTML as wiki markup language” mode, etc.
36
37 Fossil’s strong default CSP adds client-side filtering as a backstop for
38 all of this.
39
40 Fossil site administrators can [modify the default CSP](#override), perhaps
41 to add trusted external sources for auxiliary content. But for maximum
42 safety, site developers are encouraged to work within the restrictions
43 imposed by the default CSP and avoid the temptation to relax the CSP
44 unless they fully understand the security implications of what they are
45 doing.
46
47 [llaw]: https://en.wikipedia.org/wiki/Linus%27s_Law
48
49
50 ## The Default Restrictions
51
52 The Fossil default CSP declares the following content restrictions:
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
55 ### <a name="base"></a> default-src 'self' data:
56
57 This policy means mixed-origin content isn’t allowed, so you can’t refer
58 to resources on other web domains. Browsers will ignore a link like the
@@ -86,10 +76,23 @@
86 There are many other cases, [covered below](#serving).
87
88 [b64]: https://en.wikipedia.org/wiki/Base64
89 [svr]: ./server/
90
 
 
 
 
 
 
 
 
 
 
 
 
 
91
92 ### <a name="style"></a> style-src 'self' 'unsafe-inline'
93
94 This policy allows CSS information to come from separate files hosted
95 under the Fossil repo server’s Internet domain. It also allows inline CSS
@@ -106,10 +109,11 @@
106 flexibility and the work-arounds are verbose and difficult to maintain.
107 Furthermore, the harm that can be done with style injections is far
108 less than the harm possible with injected javascript. And so the
109 `'unsafe-inline'` compromise is accepted for now, though it might
110 go away in some future release of Fossil.
 
111
112 ### <a name="script"></a> script-src 'self' 'nonce-%s'
113
114 This policy disables in-line JavaScript and only allows `<script>`
115 elements if the `<script>` includes a `nonce` attribute that matches the
116
117 DDED www/delta-manifests.md
--- www/defcsp.md
+++ www/defcsp.md
@@ -1,58 +1,48 @@
1 # The Default Content Security Policy (CSP)
2
3 When Fossil’s web interface generates an HTML page, it normally includes
4 a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP specifies
5 allowed sources for external resources such as images,
6 CSS, javascript, and so froth.
7 The purpose of CSP is to provide an extra layer of protection against
8 [cross-site scripting][xss] (XSS) and code injection
9 attacks. Compatible web browsers will not use external resources unless
10 they are specifically allowed by the CSP, which dramatically reduces
11 the attack surface of the application.
12
13 Fossil does not rely on CSP for security.
14 A Fossil server should be secure from attack even with out CSP.
15 Fossil includes built-in server-side content filtering logic.
16 For example, Fossil purposely breaks `<script>` tags when it finds
17 them in Markdown and Fossil Wiki documents. And the Fossil build
18 process scans the source code for potential injection vulnerabilities
19 and refuses to compile if any problems are found.
20 However, CSP provides an additional layer of defense against undetected
21 bugs that might lead to a vulnerability.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
23 ## The Default Restrictions
24
25 The default CSP used by Fossil is as follows:
26
27 <pre>
28 default-src 'self' data:;
29 script-src 'self' 'nonce-$nonce';
30 style-src 'self' 'unsafe-inline';
31 img-src * data:;
32 </pre>
33
34 The default is recommended for most installations. However,
35 the site administrators can overwrite this default DSP using the
36 [default-csp setting](/help?cmd=default-csp). For example,
37 CSP restrictions can be completely disabled by setting the default-csp to:
38
39 <pre>
40 default-src *;
41 </pre>
42
43 The following sections detail the maining of the default CSP setting.
44
45 ### <a name="base"></a> default-src 'self' data:
46
47 This policy means mixed-origin content isn’t allowed, so you can’t refer
48 to resources on other web domains. Browsers will ignore a link like the
@@ -86,10 +76,23 @@
76 There are many other cases, [covered below](#serving).
77
78 [b64]: https://en.wikipedia.org/wiki/Base64
79 [svr]: ./server/
80
81
82 ### <a name="img"></a> img-src * data:
83
84 As of Fossil 2.15, we don’t restrict the source of inline images at all.
85 You can pull them in from remote systems as well as pull them from
86 within the Fossil repository itself, or use `data:` URIs.
87
88 If you are certain all images come from only within the repository, you
89 can close off certain risks — tracking pixels, broken image format
90 decoders, system dialog box spoofing, etc. — by changing this to
91 “`img-src 'self'`” possibly followed by “`data:`” if you will also use
92 `data:` URIs.
93
94
95 ### <a name="style"></a> style-src 'self' 'unsafe-inline'
96
97 This policy allows CSS information to come from separate files hosted
98 under the Fossil repo server’s Internet domain. It also allows inline CSS
@@ -106,10 +109,11 @@
109 flexibility and the work-arounds are verbose and difficult to maintain.
110 Furthermore, the harm that can be done with style injections is far
111 less than the harm possible with injected javascript. And so the
112 `'unsafe-inline'` compromise is accepted for now, though it might
113 go away in some future release of Fossil.
114
115
116 ### <a name="script"></a> script-src 'self' 'nonce-%s'
117
118 This policy disables in-line JavaScript and only allows `<script>`
119 elements if the `<script>` includes a `nonce` attribute that matches the
120
121 DDED www/delta-manifests.md
--- a/www/delta-manifests.md
+++ b/www/delta-manifests.md
@@ -0,0 +1,250 @@
1
+# Delta Manifests
2
+
3
+This article describes "delta manifests," a special-case form of
4
+checkin manifest which is intended to take up far less space than
5
+a normal checkin manifest, in particular for repositories with
6
+many files. We'll see, however, that the space savings, if indeed
7
+there are any, come with some caveats.
8
+
9
+This article assumes that the reader is at least moderately familiar
10
+with Fossil's [artifact file format](./fileformat.wiki), in particular
11
+the structure of checkin manifests, and it won't make much sense to
12
+readers unfamiliar with that topic.
13
+
14
+Sidebar: delta manifdebar">Do not confuse these with the core [Fossil delta
15
+format](./delta_format.wiki). This document describes an optional
16
+feature not enabled by default.</div>
17
+
18
+This article describes "delta manifests," a special-case form of
19
+checkin manifest which is intended to take up far less space than
20
+a normal checkin manifest, in particular for repositories with
21
+many files. We'll see, however, that the space savings, if indeed
22
+there are any, come with some caveats.
23
+
24
+This article assumes that the reader is at least moderately familiar
25
+with Fossil's [artifact file format](./fileformat.wiki), in particular
26
+the structure of checkin manifests, and it won't make much sense to
27
+readers unfamiliar with that topic.
28
+
29
+# Background and Motivation of Delta Manifests
30
+
31
+A checkin manifest includes a list of every file in that checkin. A
32
+moderately-sized project can easily have a thousand files, and every
33
+checkin manifest will include those thousand files. As of this writing
34
+Fossil's own checkins contain 989 files and the manifests are 80kb
35
+each. Thus a checkin which changes only 2 bytes of source code
36
+ostensibly costs another 80kb of storage for the manifest for that
37
+change.
38
+
39
+Delta manifests were conceived as a mechanism to help combat that
40
+storage overhead.
41
+
42
+# Makeup of a Delta Manifest
43
+
44
+A delta manifest is structured like a normal manifest (called a
45
+"baseline" manifest) except that it has *two types of parents*: the
46
+P-card which is part of (nearly) every manifest and a so-called
47
+baseline (denoted by a B-card). The P-card tells us which artifact(s)
48
+is/are the parents for purposes of the SCM version DAG. The B-card
49
+tells us which manifest to use as a basis for this delta. The B-card
50
+need not be, and often is not, the same as the P-card. Here's an
51
+example:
52
+
53
+```
54
+B c04ce8aaf1170966c6f8abcce8b57e72a0fa2b81
55
+C Minor\sdoc\supdates...
56
+D 2021-03-11T18:56:24.686
57
+F bindings/s2/shell_extend.c 6d8354c693120a48cfe4798812cd24499be174b2
58
+<15 F-cards snipped for brevity>
59
+F src/repo.c 2f224cb0e59ccd90ba89c597e40b8e8d87506638
60
+P 61d3e64e6fb1a93d4a7b0182e4c6b94d178d66d9
61
+R a84ec2e8e1eb37ff0d94cac262795e23
62
+U stephan
63
+Z 536e6d26dd8dbe2779d9e5f52a15518e
64
+```
65
+
66
+The B-card names another manifest, by its unique ID, the same way that
67
+a P-card does. A manifest may have multiple P-card parents (the second
68
+and subsequent ones denoting merge parents) but B-cards always refer
69
+to exactly one parent.
70
+
71
+What unambiguously distinguishes this as a delta is the existence of
72
+the B-card. All deltas have a B-card and no other type of artifact has
73
+one. What also, but not unambiguously, distinguishes it as a delta is
74
+that it has only 17 F-cards, whereas a baseline manifest in that same
75
+repository has (as of this writing) 291 F-cards. In this particular
76
+case, the delta manifest is 1363 bytes, compared to 20627 bytes for
77
+the next checkin - a baseline manifest. That's a significant saving in
78
+F-cards, especially if a repository contains thousands of files. That
79
+savings, however, comes with caveats which we'll address below.
80
+
81
+Trivia regarding the B-card:
82
+
83
+- The B-card always refers to a baseline manifest, not another delta.
84
+- Deltas may not chain with another delta, but any number of deltas
85
+ may have the same B-card. It is quite common for a series of delta
86
+ manifest checkins, each of which derives (in the P-card sense) from
87
+ the one before it, to have the same B-card.
88
+
89
+A delta manifest is functionally identical to a normal manifest except
90
+that it has a B-card and how it records F-cards. Namely, it only
91
+records F-cards which have changed at some point between this delta
92
+and the version represented by the delta's B-card. This recording of
93
+F-card *differences* also means that delta manifests, unlike normal
94
+manifests, have to explicitly record deleted F-cards. Baseline
95
+manifests do not record deletions. Instead, they include a list of
96
+every file which is part of that checkin. Deltas, however, record the
97
+differences between their own version and a baseline version, and thus
98
+have to record deletions. They do this by including F-cards which have
99
+only a file name and no hash.
100
+
101
+Iterating over F-cards in a manifest is something several important
102
+internal parts of Fossil have to do. Iterating over a baseline
103
+manifest, e.g. when performing a checkout, is straightforward: simply
104
+walk through the list in the order the cards are listed. A delta,
105
+however, introduces a significant wrinkle to that process. In short,
106
+when iterating over a delta's F-cards, code has to compare the delta's
107
+list to the baseline's list. If the delta has an entry the parent does
108
+not have, or which is a newer entry for the same file, the delta's
109
+entry is used. If the delta is missing an entry which the baseline
110
+has, the baseline's entry is used. When a deletion F-card is
111
+discovered in the delta (recall that baselines do not record
112
+deletions), iteration over that card is skipped - the internal
113
+algorithms which iterate over F-cards never report deletions to the
114
+code iterating over those cards. The reason for that is consistency:
115
+only deltas record file deletions, but the fact that it's a delta is
116
+an internal detail, not something which higher-level code should
117
+concern itself with. If higher-level iteration code were shown file
118
+deletions, they would effectively be dealing with a leaky abstraction
119
+and special-case handling which only applies to delta manifests. The
120
+F-card iteration API hides such details from its users (other
121
+Fossil-internal APIs).
122
+
123
+
124
+# When does Fossil Create Deltas?
125
+
126
+By default, Fossil never creates delta manifests. It can be told to do
127
+so using the `--delta` flag to the [`commit`
128
+command](/help/commit). (Before doing so in your own repositories,
129
+please read the section below about the caveats!) When a given
130
+repository gets a delta manifest for the first time, Fossil records
131
+that fact in the repository's `config` table with an entry named
132
+`seen-delta-manifest`. If, in later sessions, Fossil sees that that
133
+setting has a true value, it will *consider* creating delta manifests
134
+by default.
135
+
136
+Conversely, the [`forbid-delta-manifests` repository config
137
+setting](/help/forbid-delta-manifests) may be used to force Fossil to
138
+*never* create deltas. That setting will propagate to other repository
139
+clones via the sync process, to try to ensure that no clone introduces
140
+a delta manifests. We'll cover reasons why one might want to use that
141
+setting later on.
142
+
143
+After creating a delta manifest during the commit process, Fossil
144
+examines the size of the delta. If, in Fossil's opinion, the space
145
+savings are not significant enough to warrant the delta's own
146
+overhead, it will discard the delta and create a new baseline manifest
147
+instead. (The heuristic it uses for that purpose is tucked away in
148
+Fossil's checkin algorithm.)
149
+
150
+
151
+# Caveats
152
+
153
+Delta manifests may appear, on the surface, to be a great way to save
154
+a few bytes of repository space. There are, however, caveats...
155
+
156
+## Space Savings?
157
+
158
+Though deltas were conceived as a way to save storage space, that
159
+benefit is *not truly achieved* because...
160
+
161
+When a manifest is created, Fossil stores its parent version as a
162
+[fossil delta](./delta_format.wiki) (as opposed to a delta manifest)
163
+which succinctly descibes the differences between the parent and its
164
+new child. This form of compression is extremely space-efficient and
165
+can reduce the real storage space requirements of a manifest from tens
166
+or hundreds of kilobytes down to a kilobyte or less for checkins which
167
+modify only a few files. As an example, as of this writing, Fossil's
168
+[tip checkin baseline manifest](/artifact/decd537016bf) is 80252 bytes
169
+(uncompressed), and the delta-compressed baseline manifest of the
170
+[previous checkin](/artifact/2f7c93f49c0e) is stored as a mere 726
171
+bytes of Fossil-delta'd data (not counting the z-lib compression which
172
+gets applied on top of that). In this case, the tip version modified 7
173
+files compared to its parent version.
174
+
175
+Thus delta manifests do not *actually* save much storage space. They
176
+save *some*, in particular in the tip checkin version: Fossil
177
+delta-compresses *older* versions of checkins against the child
178
+versions, as opposed to delta-compressing the children against the
179
+parents. The reason is to speed up access for the most common case -
180
+the latest version. Thus tip-version delta manifests are more
181
+storage-space efficient than tip-version baseline manifests. Once the
182
+next version is committed, though, and Fossil deltification is applied
183
+to those manifests, that difference in space efficiency shrinks
184
+tremendously, often to the point of insignificance.
185
+
186
+We can observe the Fossil-delta compression savings using a bit of
187
+3rd-party code which can extract Fossil-format blobs both with and
188
+without applying their deltas:
189
+
190
+```
191
+$ f-acat tip > A # tip version's manifest
192
+$ f-acat prev --raw > B # previous manifest in its raw fossil-deltified form
193
+$ f-acat prev > C # previous manifest fossil-undelta'd
194
+$ ls -la A B C
195
+-rw-rw-r-- 1 user user 80252 Mar 12 07:09 A # tip
196
+-rw-rw-r-- 1 user user 726 Mar 12 07:09 B # previous: delta'd
197
+-rw-rw-r-- 1 user user 80256 Mar 12 07:09 C # previous: undelta'd
198
+```
199
+
200
+For comparison's sake, when looking at a separate repository which
201
+uses delta manifests, a delta-compressed delta manifest takes up
202
+approximately the same space as a delta-compressed baseline manifest
203
+(to within 10 bytes for the test samples).
204
+
205
+i.e. delta manifests may not save any storage space except for the tip
206
+version! (*Surprise!*)
207
+
208
+In terms of RAM costs, deltas usually cost more memory than baseline
209
+manifests. The reason is because traversing a delta requires having
210
+not only that delta in memory, but also its baseline version. Delta
211
+manifests are seldom used in ways which do not require also loading
212
+their baselines. Thus Fossil internally requires two manifest objects
213
+for most operations with a delta manifest, whereas a baseline has but
214
+one. The difference in RAM cost is directly proportional to the size
215
+of the delta manifest.
216
+
217
+## Manifests as Proof of Code Integrity
218
+
219
+Delta manifests have at least one more notable caveat, this one
220
+arguably more significant than an apparent lack of space savings:
221
+they're useless for purposes of publishing a manifest which downstream
222
+clients can use to verify the integrity of their copy of the software.
223
+
224
+Consider this use case: [the SQLite project](https://sqlite.org)
225
+publishes source code to many thousands of downstream consumers, many
226
+of whom would like to be able to verify that the copy they have
227
+downloaded is actually the copy published by the project. This is
228
+easily achieved by providing a copy of the downloaded version's
229
+manifest, as it contains a hash of every single file the project
230
+published and the manifest itself has a well-known hash and is
231
+cryptographically tamper-proof. It's mathematically extremely improbable for a
232
+malicious party to modify such a manifest and re-publish it as an
233
+"official" one, as the various hashes (F-cards, R-card, Z-card, *and*
234
+the hash of the manifest itself) would not line up. A collision-based
235
+attack would have to defeat *all four of those hashes*, which is
236
+practically impossible to do. Thus a Fossil checkin manifest can be used
237
+to provide strong assurances that a given copy of the software has not
238
+been tampered with since being exported by Fossil.
239
+
240
+*However*, that use case is *only possible with baseline manifests*.
241
+A delta manifest is *essentially useless* for that purpose. The
242
+algorithm for traversing F-cards of a delta manifest is not trivial
243
+for arbitrary clients to reproduce, e.g. using a shell script. While
244
+it *could* be done in any higher-level programming language (or some
245
+truly unsightly shell code), it would be an onerous burden on
246
+downstream consumers and would not be without risks of having bugs
247
+which invalidate the strong guarantees provided by the manifest.
248
+
249
+It's worth noting that the core Fossil project repository does not use
250
+del
--- a/www/delta-manifests.md
+++ b/www/delta-manifests.md
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/www/delta-manifests.md
+++ b/www/delta-manifests.md
@@ -0,0 +1,250 @@
1 # Delta Manifests
2
3 This article describes "delta manifests," a special-case form of
4 checkin manifest which is intended to take up far less space than
5 a normal checkin manifest, in particular for repositories with
6 many files. We'll see, however, that the space savings, if indeed
7 there are any, come with some caveats.
8
9 This article assumes that the reader is at least moderately familiar
10 with Fossil's [artifact file format](./fileformat.wiki), in particular
11 the structure of checkin manifests, and it won't make much sense to
12 readers unfamiliar with that topic.
13
14 Sidebar: delta manifdebar">Do not confuse these with the core [Fossil delta
15 format](./delta_format.wiki). This document describes an optional
16 feature not enabled by default.</div>
17
18 This article describes "delta manifests," a special-case form of
19 checkin manifest which is intended to take up far less space than
20 a normal checkin manifest, in particular for repositories with
21 many files. We'll see, however, that the space savings, if indeed
22 there are any, come with some caveats.
23
24 This article assumes that the reader is at least moderately familiar
25 with Fossil's [artifact file format](./fileformat.wiki), in particular
26 the structure of checkin manifests, and it won't make much sense to
27 readers unfamiliar with that topic.
28
29 # Background and Motivation of Delta Manifests
30
31 A checkin manifest includes a list of every file in that checkin. A
32 moderately-sized project can easily have a thousand files, and every
33 checkin manifest will include those thousand files. As of this writing
34 Fossil's own checkins contain 989 files and the manifests are 80kb
35 each. Thus a checkin which changes only 2 bytes of source code
36 ostensibly costs another 80kb of storage for the manifest for that
37 change.
38
39 Delta manifests were conceived as a mechanism to help combat that
40 storage overhead.
41
42 # Makeup of a Delta Manifest
43
44 A delta manifest is structured like a normal manifest (called a
45 "baseline" manifest) except that it has *two types of parents*: the
46 P-card which is part of (nearly) every manifest and a so-called
47 baseline (denoted by a B-card). The P-card tells us which artifact(s)
48 is/are the parents for purposes of the SCM version DAG. The B-card
49 tells us which manifest to use as a basis for this delta. The B-card
50 need not be, and often is not, the same as the P-card. Here's an
51 example:
52
53 ```
54 B c04ce8aaf1170966c6f8abcce8b57e72a0fa2b81
55 C Minor\sdoc\supdates...
56 D 2021-03-11T18:56:24.686
57 F bindings/s2/shell_extend.c 6d8354c693120a48cfe4798812cd24499be174b2
58 <15 F-cards snipped for brevity>
59 F src/repo.c 2f224cb0e59ccd90ba89c597e40b8e8d87506638
60 P 61d3e64e6fb1a93d4a7b0182e4c6b94d178d66d9
61 R a84ec2e8e1eb37ff0d94cac262795e23
62 U stephan
63 Z 536e6d26dd8dbe2779d9e5f52a15518e
64 ```
65
66 The B-card names another manifest, by its unique ID, the same way that
67 a P-card does. A manifest may have multiple P-card parents (the second
68 and subsequent ones denoting merge parents) but B-cards always refer
69 to exactly one parent.
70
71 What unambiguously distinguishes this as a delta is the existence of
72 the B-card. All deltas have a B-card and no other type of artifact has
73 one. What also, but not unambiguously, distinguishes it as a delta is
74 that it has only 17 F-cards, whereas a baseline manifest in that same
75 repository has (as of this writing) 291 F-cards. In this particular
76 case, the delta manifest is 1363 bytes, compared to 20627 bytes for
77 the next checkin - a baseline manifest. That's a significant saving in
78 F-cards, especially if a repository contains thousands of files. That
79 savings, however, comes with caveats which we'll address below.
80
81 Trivia regarding the B-card:
82
83 - The B-card always refers to a baseline manifest, not another delta.
84 - Deltas may not chain with another delta, but any number of deltas
85 may have the same B-card. It is quite common for a series of delta
86 manifest checkins, each of which derives (in the P-card sense) from
87 the one before it, to have the same B-card.
88
89 A delta manifest is functionally identical to a normal manifest except
90 that it has a B-card and how it records F-cards. Namely, it only
91 records F-cards which have changed at some point between this delta
92 and the version represented by the delta's B-card. This recording of
93 F-card *differences* also means that delta manifests, unlike normal
94 manifests, have to explicitly record deleted F-cards. Baseline
95 manifests do not record deletions. Instead, they include a list of
96 every file which is part of that checkin. Deltas, however, record the
97 differences between their own version and a baseline version, and thus
98 have to record deletions. They do this by including F-cards which have
99 only a file name and no hash.
100
101 Iterating over F-cards in a manifest is something several important
102 internal parts of Fossil have to do. Iterating over a baseline
103 manifest, e.g. when performing a checkout, is straightforward: simply
104 walk through the list in the order the cards are listed. A delta,
105 however, introduces a significant wrinkle to that process. In short,
106 when iterating over a delta's F-cards, code has to compare the delta's
107 list to the baseline's list. If the delta has an entry the parent does
108 not have, or which is a newer entry for the same file, the delta's
109 entry is used. If the delta is missing an entry which the baseline
110 has, the baseline's entry is used. When a deletion F-card is
111 discovered in the delta (recall that baselines do not record
112 deletions), iteration over that card is skipped - the internal
113 algorithms which iterate over F-cards never report deletions to the
114 code iterating over those cards. The reason for that is consistency:
115 only deltas record file deletions, but the fact that it's a delta is
116 an internal detail, not something which higher-level code should
117 concern itself with. If higher-level iteration code were shown file
118 deletions, they would effectively be dealing with a leaky abstraction
119 and special-case handling which only applies to delta manifests. The
120 F-card iteration API hides such details from its users (other
121 Fossil-internal APIs).
122
123
124 # When does Fossil Create Deltas?
125
126 By default, Fossil never creates delta manifests. It can be told to do
127 so using the `--delta` flag to the [`commit`
128 command](/help/commit). (Before doing so in your own repositories,
129 please read the section below about the caveats!) When a given
130 repository gets a delta manifest for the first time, Fossil records
131 that fact in the repository's `config` table with an entry named
132 `seen-delta-manifest`. If, in later sessions, Fossil sees that that
133 setting has a true value, it will *consider* creating delta manifests
134 by default.
135
136 Conversely, the [`forbid-delta-manifests` repository config
137 setting](/help/forbid-delta-manifests) may be used to force Fossil to
138 *never* create deltas. That setting will propagate to other repository
139 clones via the sync process, to try to ensure that no clone introduces
140 a delta manifests. We'll cover reasons why one might want to use that
141 setting later on.
142
143 After creating a delta manifest during the commit process, Fossil
144 examines the size of the delta. If, in Fossil's opinion, the space
145 savings are not significant enough to warrant the delta's own
146 overhead, it will discard the delta and create a new baseline manifest
147 instead. (The heuristic it uses for that purpose is tucked away in
148 Fossil's checkin algorithm.)
149
150
151 # Caveats
152
153 Delta manifests may appear, on the surface, to be a great way to save
154 a few bytes of repository space. There are, however, caveats...
155
156 ## Space Savings?
157
158 Though deltas were conceived as a way to save storage space, that
159 benefit is *not truly achieved* because...
160
161 When a manifest is created, Fossil stores its parent version as a
162 [fossil delta](./delta_format.wiki) (as opposed to a delta manifest)
163 which succinctly descibes the differences between the parent and its
164 new child. This form of compression is extremely space-efficient and
165 can reduce the real storage space requirements of a manifest from tens
166 or hundreds of kilobytes down to a kilobyte or less for checkins which
167 modify only a few files. As an example, as of this writing, Fossil's
168 [tip checkin baseline manifest](/artifact/decd537016bf) is 80252 bytes
169 (uncompressed), and the delta-compressed baseline manifest of the
170 [previous checkin](/artifact/2f7c93f49c0e) is stored as a mere 726
171 bytes of Fossil-delta'd data (not counting the z-lib compression which
172 gets applied on top of that). In this case, the tip version modified 7
173 files compared to its parent version.
174
175 Thus delta manifests do not *actually* save much storage space. They
176 save *some*, in particular in the tip checkin version: Fossil
177 delta-compresses *older* versions of checkins against the child
178 versions, as opposed to delta-compressing the children against the
179 parents. The reason is to speed up access for the most common case -
180 the latest version. Thus tip-version delta manifests are more
181 storage-space efficient than tip-version baseline manifests. Once the
182 next version is committed, though, and Fossil deltification is applied
183 to those manifests, that difference in space efficiency shrinks
184 tremendously, often to the point of insignificance.
185
186 We can observe the Fossil-delta compression savings using a bit of
187 3rd-party code which can extract Fossil-format blobs both with and
188 without applying their deltas:
189
190 ```
191 $ f-acat tip > A # tip version's manifest
192 $ f-acat prev --raw > B # previous manifest in its raw fossil-deltified form
193 $ f-acat prev > C # previous manifest fossil-undelta'd
194 $ ls -la A B C
195 -rw-rw-r-- 1 user user 80252 Mar 12 07:09 A # tip
196 -rw-rw-r-- 1 user user 726 Mar 12 07:09 B # previous: delta'd
197 -rw-rw-r-- 1 user user 80256 Mar 12 07:09 C # previous: undelta'd
198 ```
199
200 For comparison's sake, when looking at a separate repository which
201 uses delta manifests, a delta-compressed delta manifest takes up
202 approximately the same space as a delta-compressed baseline manifest
203 (to within 10 bytes for the test samples).
204
205 i.e. delta manifests may not save any storage space except for the tip
206 version! (*Surprise!*)
207
208 In terms of RAM costs, deltas usually cost more memory than baseline
209 manifests. The reason is because traversing a delta requires having
210 not only that delta in memory, but also its baseline version. Delta
211 manifests are seldom used in ways which do not require also loading
212 their baselines. Thus Fossil internally requires two manifest objects
213 for most operations with a delta manifest, whereas a baseline has but
214 one. The difference in RAM cost is directly proportional to the size
215 of the delta manifest.
216
217 ## Manifests as Proof of Code Integrity
218
219 Delta manifests have at least one more notable caveat, this one
220 arguably more significant than an apparent lack of space savings:
221 they're useless for purposes of publishing a manifest which downstream
222 clients can use to verify the integrity of their copy of the software.
223
224 Consider this use case: [the SQLite project](https://sqlite.org)
225 publishes source code to many thousands of downstream consumers, many
226 of whom would like to be able to verify that the copy they have
227 downloaded is actually the copy published by the project. This is
228 easily achieved by providing a copy of the downloaded version's
229 manifest, as it contains a hash of every single file the project
230 published and the manifest itself has a well-known hash and is
231 cryptographically tamper-proof. It's mathematically extremely improbable for a
232 malicious party to modify such a manifest and re-publish it as an
233 "official" one, as the various hashes (F-cards, R-card, Z-card, *and*
234 the hash of the manifest itself) would not line up. A collision-based
235 attack would have to defeat *all four of those hashes*, which is
236 practically impossible to do. Thus a Fossil checkin manifest can be used
237 to provide strong assurances that a given copy of the software has not
238 been tampered with since being exported by Fossil.
239
240 *However*, that use case is *only possible with baseline manifests*.
241 A delta manifest is *essentially useless* for that purpose. The
242 algorithm for traversing F-cards of a delta manifest is not trivial
243 for arbitrary clients to reproduce, e.g. using a shell script. While
244 it *could* be done in any higher-level programming language (or some
245 truly unsightly shell code), it would be an onerous burden on
246 downstream consumers and would not be without risks of having bugs
247 which invalidate the strong guarantees provided by the manifest.
248
249 It's worth noting that the core Fossil project repository does not use
250 del
+69 -27
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -8,55 +8,97 @@
88
This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org)
99
and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations
1010
have an identical implementation of the Fossil data model, are 100% compatible in terms of
1111
data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage.
1212
13
-## General Features
13
+# General Features
1414
15
-* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
1615
* Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
1716
* Allow diffing of Forum posts
1817
* Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
1918
* Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
2019
* Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples
2120
* Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown
2221
* Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown)
2322
* Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31)
2423
25
-## Add code to handle email bounces
26
-
27
-Fossil can [send email alerts](./alerts.md), but cannot receive email at all. That is a good thing, because a
28
-complete [SMTP MTA](https://en.wikipedia.org/wiki/MTA) is complicated and requires constant maintenance. There
29
-is one specific case where receiving mail in some fashion would help, and that is for handling bounce messages
30
-from invalid email addresses.
31
-
32
-A proposal for that is to implement a Fossil command such as:
33
-
34
-```
35
-fossil email -R repo receive_bounce
36
-```
37
-
38
-This is a non-network-aware Mail Delivery Agent, and would be called by an MTA such as Postfix, Courier or Exim.
39
-This command would reject anything that doesn't look like a bounce it is expecting.
40
-
41
-## Work relating to the ticketing system in Fossil
24
+# Adding Inbound (Receiving) Email to Fossil
25
+
26
+This task involves designing a new feature and working with Fossil developers to
27
+see how it can be feasible in practice.
28
+
29
+Fossil can [send email alerts](./alerts.md), but cannot receive email at all.
30
+That is a good thing, because a complete [SMTP
31
+MTA](https://en.wikipedia.org/wiki/MTA) is complicated and requires constant
32
+maintenance, so Fossil should not try to be an MTA or ever listen to mail ports
33
+on the Internet.
34
+
35
+There is one specific type of email reception that make sense for Fossil to
36
+handle. When there is inbound mail related to a message that Fossil has
37
+previously generated with a unique hash, Fossil already knows the context of
38
+that message. An unknown sender cannot guess a valid hash although a malicious
39
+sender could of course find a way to receive a valid hash and then use that to
40
+gain access. The risk of automatic and non-specific spam is very low.
41
+
42
+A proposal to handle that would be to implement a Fossil command like this:
43
+
44
+```
45
+fossil email -R repo receive -t TYPE-OF-EMAIL -h HASH
46
+```
47
+
48
+Where the type of email would be one of a list something like this:
49
+
50
+* mail_bounce
51
+* ticket_reply
52
+* forum_reply
53
+
54
+This command is a non-network-aware [Mail Delivery
55
+Agent](https://en.wikipedia.org/wiki/Mail_delivery_agent), and would be called
56
+by an SMTP MTA such as Postfix, Courier or Exim. The MTA would need to be
57
+configured to recognise that this is an email intended for Fossil, and what
58
+type of email, and to extract its hash. People who configure MTAs are used to
59
+doing this sort of thing, but no doubt Fossil would include a sample
60
+[Postfix mail filter](http://www.postfix.org/FILTER_README.html#simple_filter) and
61
+an equivalent driver for Exim.
62
+
63
+The Fossil command would reject anything that doesn't look like a bounce it is expecting.
64
+
65
+It is not certain that this design is the best one to address the inbound mail
66
+problem. That is why the first part of this task is to find a workable design.
67
+
68
+# Work relating to the ticketing system in Fossil
4269
4370
The Fossil SCM project uses tickets in a [somewhat unusual manner](https://fossil-scm.org/home/reportlist)
4471
because the social programming
45
-model has evolved to often use the Fosum instead. Other Fossil-using projects
72
+model has evolved to often use the Forum instead of ticketing. Other Fossil-using projects
4673
use tickets in a more traditional report-a-bug manner. So this means that the
47
-Fossil ticketing system user interface is underdeveloped. On the other hand,
48
-pretty much every software developer uses a ticketing system at some point in
49
-their workflow, and Fossil is intended to be usable by most developers. The
50
-underlying technology for the Fossil ticketing system is guaranteed, so to
51
-improve it requires only user interface changes.
74
+Fossil ticketing system user interface is underdeveloped.
75
+
76
+On the other hand, pretty much every software developer uses a ticketing system
77
+at some point in their workflow, and Fossil is intended to be usable by most
78
+developers. That means the ticketing system really needs to be further
79
+developed. The underlying technology for the Fossil ticketing system is
80
+guaranteed, so to improve it requires only user interface changes.
5281
5382
Projects relating to the ticketing system include:
5483
55
-* Improving the [Fossil cli for tickets](https://fossil-scm.org/forum/forumpost/d8e8a1cf92) which is confusing, as pointed out in that ticket.
84
+* Improving the [Fossil cli for tickets](https://fossil-scm.org/forum/forumpost/d8e8a1cf92) which is confusing, as pointed out in that ticket. This is still classified as a "user interface" even though it isn't graphical.
5685
* Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory.
57
-* Improving the Fossil web UI for ticketing, which is clunky to say the least
86
+* Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing,
87
+and have UI features that would improve ticketing
88
+* If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing
89
+
90
+# Look and Feel
91
+
92
+Tasks for those interested in graphic/web design:
93
+
94
+* General touch-ups in the existing skins. This may, depending on how deep one
95
+ cares to dig, require digging into C code to find, and potentially modify, how
96
+ the HTML is generated.
97
+* Creation of one or more new skins. This does not specifically require any C
98
+ know-how.
99
+* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
58100
59101
# Tasks Requiring Fossil Data Model Knowledge
60102
61103
The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model
62104
is designed to [endure for centuries](./fileformat.wiki),
63105
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -8,55 +8,97 @@
8 This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org)
9 and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations
10 have an identical implementation of the Fossil data model, are 100% compatible in terms of
11 data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage.
12
13 ## General Features
14
15 * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
16 * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
17 * Allow diffing of Forum posts
18 * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
19 * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
20 * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples
21 * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown
22 * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown)
23 * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31)
24
25 ## Add code to handle email bounces
26
27 Fossil can [send email alerts](./alerts.md), but cannot receive email at all. That is a good thing, because a
28 complete [SMTP MTA](https://en.wikipedia.org/wiki/MTA) is complicated and requires constant maintenance. There
29 is one specific case where receiving mail in some fashion would help, and that is for handling bounce messages
30 from invalid email addresses.
31
32 A proposal for that is to implement a Fossil command such as:
33
34 ```
35 fossil email -R repo receive_bounce
36 ```
37
38 This is a non-network-aware Mail Delivery Agent, and would be called by an MTA such as Postfix, Courier or Exim.
39 This command would reject anything that doesn't look like a bounce it is expecting.
40
41 ## Work relating to the ticketing system in Fossil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
43 The Fossil SCM project uses tickets in a [somewhat unusual manner](https://fossil-scm.org/home/reportlist)
44 because the social programming
45 model has evolved to often use the Fosum instead. Other Fossil-using projects
46 use tickets in a more traditional report-a-bug manner. So this means that the
47 Fossil ticketing system user interface is underdeveloped. On the other hand,
48 pretty much every software developer uses a ticketing system at some point in
49 their workflow, and Fossil is intended to be usable by most developers. The
50 underlying technology for the Fossil ticketing system is guaranteed, so to
51 improve it requires only user interface changes.
 
 
52
53 Projects relating to the ticketing system include:
54
55 * Improving the [Fossil cli for tickets](https://fossil-scm.org/forum/forumpost/d8e8a1cf92) which is confusing, as pointed out in that ticket.
56 * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory.
57 * Improving the Fossil web UI for ticketing, which is clunky to say the least
 
 
 
 
 
 
 
 
 
 
 
 
 
58
59 # Tasks Requiring Fossil Data Model Knowledge
60
61 The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model
62 is designed to [endure for centuries](./fileformat.wiki),
63
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -8,55 +8,97 @@
8 This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org)
9 and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations
10 have an identical implementation of the Fossil data model, are 100% compatible in terms of
11 data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage.
12
13 # General Features
14
 
15 * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
16 * Allow diffing of Forum posts
17 * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
18 * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
19 * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples
20 * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown
21 * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown)
22 * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31)
23
24 # Adding Inbound (Receiving) Email to Fossil
25
26 This task involves designing a new feature and working with Fossil developers to
27 see how it can be feasible in practice.
28
29 Fossil can [send email alerts](./alerts.md), but cannot receive email at all.
30 That is a good thing, because a complete [SMTP
31 MTA](https://en.wikipedia.org/wiki/MTA) is complicated and requires constant
32 maintenance, so Fossil should not try to be an MTA or ever listen to mail ports
33 on the Internet.
34
35 There is one specific type of email reception that make sense for Fossil to
36 handle. When there is inbound mail related to a message that Fossil has
37 previously generated with a unique hash, Fossil already knows the context of
38 that message. An unknown sender cannot guess a valid hash although a malicious
39 sender could of course find a way to receive a valid hash and then use that to
40 gain access. The risk of automatic and non-specific spam is very low.
41
42 A proposal to handle that would be to implement a Fossil command like this:
43
44 ```
45 fossil email -R repo receive -t TYPE-OF-EMAIL -h HASH
46 ```
47
48 Where the type of email would be one of a list something like this:
49
50 * mail_bounce
51 * ticket_reply
52 * forum_reply
53
54 This command is a non-network-aware [Mail Delivery
55 Agent](https://en.wikipedia.org/wiki/Mail_delivery_agent), and would be called
56 by an SMTP MTA such as Postfix, Courier or Exim. The MTA would need to be
57 configured to recognise that this is an email intended for Fossil, and what
58 type of email, and to extract its hash. People who configure MTAs are used to
59 doing this sort of thing, but no doubt Fossil would include a sample
60 [Postfix mail filter](http://www.postfix.org/FILTER_README.html#simple_filter) and
61 an equivalent driver for Exim.
62
63 The Fossil command would reject anything that doesn't look like a bounce it is expecting.
64
65 It is not certain that this design is the best one to address the inbound mail
66 problem. That is why the first part of this task is to find a workable design.
67
68 # Work relating to the ticketing system in Fossil
69
70 The Fossil SCM project uses tickets in a [somewhat unusual manner](https://fossil-scm.org/home/reportlist)
71 because the social programming
72 model has evolved to often use the Forum instead of ticketing. Other Fossil-using projects
73 use tickets in a more traditional report-a-bug manner. So this means that the
74 Fossil ticketing system user interface is underdeveloped.
75
76 On the other hand, pretty much every software developer uses a ticketing system
77 at some point in their workflow, and Fossil is intended to be usable by most
78 developers. That means the ticketing system really needs to be further
79 developed. The underlying technology for the Fossil ticketing system is
80 guaranteed, so to improve it requires only user interface changes.
81
82 Projects relating to the ticketing system include:
83
84 * Improving the [Fossil cli for tickets](https://fossil-scm.org/forum/forumpost/d8e8a1cf92) which is confusing, as pointed out in that ticket. This is still classified as a "user interface" even though it isn't graphical.
85 * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory.
86 * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing,
87 and have UI features that would improve ticketing
88 * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing
89
90 # Look and Feel
91
92 Tasks for those interested in graphic/web design:
93
94 * General touch-ups in the existing skins. This may, depending on how deep one
95 cares to dig, require digging into C code to find, and potentially modify, how
96 the HTML is generated.
97 * Creation of one or more new skins. This does not specifically require any C
98 know-how.
99 * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
100
101 # Tasks Requiring Fossil Data Model Knowledge
102
103 The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model
104 is designed to [endure for centuries](./fileformat.wiki),
105
+2 -2
--- www/index.wiki
+++ www/index.wiki
@@ -95,12 +95,12 @@
9595
<hr>
9696
<h3>Quick Start</h3>
9797
9898
1. [/uv/download.html|Download] or install using a package manager or
9999
[./build.wiki|compile from sources].
100
- 2. <tt>fossil init</tt> <i>new-repository</i>
101
- 3. <tt>fossil open</tt> <i>new-repository</i>
100
+ 2. <tt>fossil init</tt> <i>REPOSTORE-DIR/new-repository</i>
101
+ 3. <tt>fossil open</tt> <i>REPOSTORE-DIR/new-repository</i>
102102
4. <tt>fossil add</tt> <i>files-or-directories</i>
103103
5. <tt>fossil commit -m</tt> "<i>commit message</i>"
104104
6. <tt>fossil ui</tt>
105105
7. Repeat steps 4, 5, and 6, in any order, as necessary.
106106
See the [./quickstart.wiki|Quick Start Guide] for more detail.
107107
--- www/index.wiki
+++ www/index.wiki
@@ -95,12 +95,12 @@
95 <hr>
96 <h3>Quick Start</h3>
97
98 1. [/uv/download.html|Download] or install using a package manager or
99 [./build.wiki|compile from sources].
100 2. <tt>fossil init</tt> <i>new-repository</i>
101 3. <tt>fossil open</tt> <i>new-repository</i>
102 4. <tt>fossil add</tt> <i>files-or-directories</i>
103 5. <tt>fossil commit -m</tt> "<i>commit message</i>"
104 6. <tt>fossil ui</tt>
105 7. Repeat steps 4, 5, and 6, in any order, as necessary.
106 See the [./quickstart.wiki|Quick Start Guide] for more detail.
107
--- www/index.wiki
+++ www/index.wiki
@@ -95,12 +95,12 @@
95 <hr>
96 <h3>Quick Start</h3>
97
98 1. [/uv/download.html|Download] or install using a package manager or
99 [./build.wiki|compile from sources].
100 2. <tt>fossil init</tt> <i>REPOSTORE-DIR/new-repository</i>
101 3. <tt>fossil open</tt> <i>REPOSTORE-DIR/new-repository</i>
102 4. <tt>fossil add</tt> <i>files-or-directories</i>
103 5. <tt>fossil commit -m</tt> "<i>commit message</i>"
104 6. <tt>fossil ui</tt>
105 7. Repeat steps 4, 5, and 6, in any order, as necessary.
106 See the [./quickstart.wiki|Quick Start Guide] for more detail.
107
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -94,11 +94,11 @@
9494
Running this Tcl script will automatically regenerate all makefiles.
9595
In order to add a new source file to the Fossil implementation, simply
9696
edit makemake.tcl to add the new filename, then rerun the script, and
9797
all of the makefiles for all targets will be rebuild.
9898
99
-There is an option code verification step implemented using
99
+There is an optional code verification step implemented using
100100
101101
15. [/file/src/codecheck1.c | codecheck1.c]
102102
103103
This file implements a small utility program ("codecheck1")
104104
that scans other Fossil source files looking for errors in printf-style
105105
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -94,11 +94,11 @@
94 Running this Tcl script will automatically regenerate all makefiles.
95 In order to add a new source file to the Fossil implementation, simply
96 edit makemake.tcl to add the new filename, then rerun the script, and
97 all of the makefiles for all targets will be rebuild.
98
99 There is an option code verification step implemented using
100
101 15. [/file/src/codecheck1.c | codecheck1.c]
102
103 This file implements a small utility program ("codecheck1")
104 that scans other Fossil source files looking for errors in printf-style
105
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -94,11 +94,11 @@
94 Running this Tcl script will automatically regenerate all makefiles.
95 In order to add a new source file to the Fossil implementation, simply
96 edit makemake.tcl to add the new filename, then rerun the script, and
97 all of the makefiles for all targets will be rebuild.
98
99 There is an optional code verification step implemented using
100
101 15. [/file/src/codecheck1.c | codecheck1.c]
102
103 This file implements a small utility program ("codecheck1")
104 that scans other Fossil source files looking for errors in printf-style
105
+17 -10
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -3,10 +3,14 @@
33
# Run this TCL script to generate a WIKI page that contains a
44
# permuted index of the various documentation files.
55
#
66
# tclsh mkindex.tcl
77
#
8
+# 2021-02-26: The permuted index feature has been removed because
9
+# moderns don't understand such things, and seeing so many entries
10
+# confuses them.
11
+#
812
913
set doclist {
1014
aboutcgi.wiki {How CGI Works In Fossil}
1115
aboutdownload.wiki {How The Download Page Works}
1216
adding_code.wiki {Adding New Features To Fossil}
@@ -41,10 +45,11 @@
4145
customgraph.md {Theming: Customizing the Timeline Graph}
4246
customskin.md {Theming: Customizing The Appearance of Web Pages}
4347
customskin.md {Custom Skins}
4448
custom_ticket.wiki {Customizing The Ticket System}
4549
defcsp.md {The Default Content Security Policy}
50
+ delta-manifests.md {Delta Manifests}
4651
delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm}
4752
delta_format.wiki {Fossil Delta Format}
4853
embeddeddoc.wiki {Embedded Project Documentation}
4954
encryptedrepos.wiki {How To Use Encrypted Repositories}
5055
env-opts.md {Environment Variables and Global Options}
@@ -125,18 +130,20 @@
125130
}
126131
foreach {file title} $doclist {
127132
set n [llength $title]
128133
regsub -all {\s+} $title { } title
129134
lappend permindex [list $title $file 1]
130
- for {set i 0} {$i<$n-1} {incr i} {
131
- set prefix [lrange $title 0 $i]
132
- set suffix [lrange $title [expr {$i+1}] end]
133
- set firstword [string tolower [lindex $suffix 0]]
134
- if {[lsearch $stopwords $firstword]<0} {
135
- lappend permindex [list "$suffix &mdash; $prefix" $file 0]
136
- }
137
- }
135
+
136
+# Disable the permutations.
137
+# for {set i 0} {$i<$n-1} {incr i} {
138
+# set prefix [lrange $title 0 $i]
139
+# set suffix [lrange $title [expr {$i+1}] end]
140
+# set firstword [string tolower [lindex $suffix 0]]
141
+# if {[lsearch $stopwords $firstword]<0} {
142
+# lappend permindex [list "$suffix &mdash; $prefix" $file 0]
143
+# }
144
+# }
138145
}
139146
set permindex [lsort -dict -index 0 $permindex]
140147
set out [open permutedindex.html w]
141148
fconfigure $out -encoding utf-8 -translation lf
142149
puts $out \
@@ -160,14 +167,14 @@
160167
<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
161168
<li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's
162169
book</a>
163170
</ul>
164171
<a name="pindex"></a>
165
-<h2>Permuted Index:</h2>
172
+<h2>Other Documents:</h2>
166173
<ul>}
167174
foreach entry $permindex {
168175
foreach {title file bold} $entry break
169
- if {$bold} {set title <b>$title</b>}
176
+# if {$bold} {set title <b>$title</b>}
170177
if {[string match /* $file]} {set file ../../..$file}
171178
puts $out "<li><a href=\"$file\">$title</a></li>"
172179
}
173180
puts $out "</ul></div>"
174181
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -3,10 +3,14 @@
3 # Run this TCL script to generate a WIKI page that contains a
4 # permuted index of the various documentation files.
5 #
6 # tclsh mkindex.tcl
7 #
 
 
 
 
8
9 set doclist {
10 aboutcgi.wiki {How CGI Works In Fossil}
11 aboutdownload.wiki {How The Download Page Works}
12 adding_code.wiki {Adding New Features To Fossil}
@@ -41,10 +45,11 @@
41 customgraph.md {Theming: Customizing the Timeline Graph}
42 customskin.md {Theming: Customizing The Appearance of Web Pages}
43 customskin.md {Custom Skins}
44 custom_ticket.wiki {Customizing The Ticket System}
45 defcsp.md {The Default Content Security Policy}
 
46 delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm}
47 delta_format.wiki {Fossil Delta Format}
48 embeddeddoc.wiki {Embedded Project Documentation}
49 encryptedrepos.wiki {How To Use Encrypted Repositories}
50 env-opts.md {Environment Variables and Global Options}
@@ -125,18 +130,20 @@
125 }
126 foreach {file title} $doclist {
127 set n [llength $title]
128 regsub -all {\s+} $title { } title
129 lappend permindex [list $title $file 1]
130 for {set i 0} {$i<$n-1} {incr i} {
131 set prefix [lrange $title 0 $i]
132 set suffix [lrange $title [expr {$i+1}] end]
133 set firstword [string tolower [lindex $suffix 0]]
134 if {[lsearch $stopwords $firstword]<0} {
135 lappend permindex [list "$suffix &mdash; $prefix" $file 0]
136 }
137 }
 
 
138 }
139 set permindex [lsort -dict -index 0 $permindex]
140 set out [open permutedindex.html w]
141 fconfigure $out -encoding utf-8 -translation lf
142 puts $out \
@@ -160,14 +167,14 @@
160 <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
161 <li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's
162 book</a>
163 </ul>
164 <a name="pindex"></a>
165 <h2>Permuted Index:</h2>
166 <ul>}
167 foreach entry $permindex {
168 foreach {title file bold} $entry break
169 if {$bold} {set title <b>$title</b>}
170 if {[string match /* $file]} {set file ../../..$file}
171 puts $out "<li><a href=\"$file\">$title</a></li>"
172 }
173 puts $out "</ul></div>"
174
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -3,10 +3,14 @@
3 # Run this TCL script to generate a WIKI page that contains a
4 # permuted index of the various documentation files.
5 #
6 # tclsh mkindex.tcl
7 #
8 # 2021-02-26: The permuted index feature has been removed because
9 # moderns don't understand such things, and seeing so many entries
10 # confuses them.
11 #
12
13 set doclist {
14 aboutcgi.wiki {How CGI Works In Fossil}
15 aboutdownload.wiki {How The Download Page Works}
16 adding_code.wiki {Adding New Features To Fossil}
@@ -41,10 +45,11 @@
45 customgraph.md {Theming: Customizing the Timeline Graph}
46 customskin.md {Theming: Customizing The Appearance of Web Pages}
47 customskin.md {Custom Skins}
48 custom_ticket.wiki {Customizing The Ticket System}
49 defcsp.md {The Default Content Security Policy}
50 delta-manifests.md {Delta Manifests}
51 delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm}
52 delta_format.wiki {Fossil Delta Format}
53 embeddeddoc.wiki {Embedded Project Documentation}
54 encryptedrepos.wiki {How To Use Encrypted Repositories}
55 env-opts.md {Environment Variables and Global Options}
@@ -125,18 +130,20 @@
130 }
131 foreach {file title} $doclist {
132 set n [llength $title]
133 regsub -all {\s+} $title { } title
134 lappend permindex [list $title $file 1]
135
136 # Disable the permutations.
137 # for {set i 0} {$i<$n-1} {incr i} {
138 # set prefix [lrange $title 0 $i]
139 # set suffix [lrange $title [expr {$i+1}] end]
140 # set firstword [string tolower [lindex $suffix 0]]
141 # if {[lsearch $stopwords $firstword]<0} {
142 # lappend permindex [list "$suffix &mdash; $prefix" $file 0]
143 # }
144 # }
145 }
146 set permindex [lsort -dict -index 0 $permindex]
147 set out [open permutedindex.html w]
148 fconfigure $out -encoding utf-8 -translation lf
149 puts $out \
@@ -160,14 +167,14 @@
167 <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
168 <li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's
169 book</a>
170 </ul>
171 <a name="pindex"></a>
172 <h2>Other Documents:</h2>
173 <ul>}
174 foreach entry $permindex {
175 foreach {title file bold} $entry break
176 # if {$bold} {set title <b>$title</b>}
177 if {[string match /* $file]} {set file ../../..$file}
178 puts $out "<li><a href=\"$file\">$title</a></li>"
179 }
180 puts $out "</ul></div>"
181
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -18,349 +18,116 @@
1818
<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
1919
<li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's
2020
book</a>
2121
</ul>
2222
<a name="pindex"></a>
23
-<h2>Permuted Index:</h2>
23
+<h2>Other Documents:</h2>
2424
<ul>
25
-<li><a href="fossil-is-not-relational.md">(Non-relational) Fossil Data Model &mdash; Introduction to the</a></li>
26
-<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
27
-<li><a href="fossil-from-msvc.wiki">2010 IDE &mdash; Integrating Fossil in the Microsoft Express</a></li>
28
-<li><a href="tech_overview.wiki"><b>A Technical Overview Of The Design And Implementation Of Fossil</b></a></li>
29
-<li><a href="serverext.wiki"><b>Adding Extensions To A Fossil Server Using CGI Scripts</b></a></li>
30
-<li><a href="adding_code.wiki"><b>Adding New Features To Fossil</b></a></li>
31
-<li><a href="caps/admin-v-setup.md">Admin Users &mdash; Differences Between Setup and</a></li>
32
-<li><a href="caps/"><b>Administering User Capabilities</b></a></li>
33
-<li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
34
-<li><a href="alerts.md">Alerts And Notifications &mdash; Email</a></li>
35
-<li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
36
-<li><a href="blame.wiki">Algorithm Of Fossil &mdash; The Annotate/Blame</a></li>
37
-<li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil &mdash; The</a></li>
38
-<li><a href="customskin.md">Appearance of Web Pages &mdash; Theming: Customizing The</a></li>
39
-<li><a href="hashes.md">Artifact Identification &mdash; Hashes: Fossil</a></li>
40
-<li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
41
-<li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
42
-<li><a href="backup.md"><b>Backing Up a Remote Fossil Repository</b></a></li>
43
-<li><a href="backoffice.md">Backoffice mechanism of Fossil &mdash; The</a></li>
44
-<li><a href="fossil_prompt.wiki">Bash Prompt &mdash; Fossilized</a></li>
45
-<li><a href="whyusefossil.wiki"><b>Benefits Of Version Control</b></a></li>
46
-<li><a href="caps/admin-v-setup.md">Between Setup and Admin Users &mdash; Differences</a></li>
47
-<li><a href="hashpolicy.wiki">Between SHA1 and SHA3-256 &mdash; Hash Policy: Choosing</a></li>
48
-<li><a href="blockchain.md">Blockchain? &mdash; Is Fossil A</a></li>
49
-<li><a href="antibot.wiki">Bots &mdash; Defense against Spiders and</a></li>
50
-<li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
51
-<li><a href="branching.wiki"><b>Branching, Forking, Merging, and Tagging</b></a></li>
52
-<li><a href="bugtheory.wiki"><b>Bug Tracking In Fossil</b></a></li>
53
-<li><a href="makefile.wiki">Build Process &mdash; The Fossil</a></li>
54
-<li><a href="cap-theorem.md">CAP Theorem &mdash; Fossil and the</a></li>
55
-<li><a href="caps/">Capabilities &mdash; Administering User</a></li>
56
-<li><a href="caps/ref.html">Capability Reference &mdash; User</a></li>
57
-<li><a href="cgi.wiki"><b>CGI Script Configuration Options</b></a></li>
58
-<li><a href="serverext.wiki">CGI Scripts &mdash; Adding Extensions To A Fossil Server Using</a></li>
59
-<li><a href="serverext.wiki"><b>CGI Server Extensions</b></a></li>
60
-<li><a href="aboutcgi.wiki">CGI Works In Fossil &mdash; How</a></li>
61
-<li><a href="changes.wiki">Changelog &mdash; Fossil</a></li>
62
-<li><a href="chat.md">Chat &mdash; Fossil</a></li>
63
-<li><a href="checkin_names.wiki"><b>Check-in And Version Names</b></a></li>
64
-<li><a href="checkin.wiki"><b>Check-in Checklist</b></a></li>
65
-<li><a href="ckout-workflows.md"><b>Check-Out Workflows</b></a></li>
66
-<li><a href="checkin.wiki">Checklist &mdash; Check-in</a></li>
67
-<li><a href="../test/release-checklist.wiki">Checklist &mdash; Pre-Release Testing</a></li>
68
-<li><a href="foss-cklist.wiki"><b>Checklist For Successful Open-Source Projects</b></a></li>
69
-<li><a href="co-vs-up.md"><b>Checkout vs Update</b></a></li>
70
-<li><a href="selfcheck.wiki">Checks &mdash; Fossil Repository Integrity Self</a></li>
71
-<li><a href="childprojects.wiki"><b>Child Projects</b></a></li>
72
-<li><a href="hashpolicy.wiki">Choosing Between SHA1 and SHA3-256 &mdash; Hash Policy:</a></li>
73
-<li><a href="chroot.md">Chroot Jail &mdash; Server</a></li>
74
-<li><a href="contribute.wiki">Code or Documentation To The Fossil Project &mdash; Contributing</a></li>
75
-<li><a href="style.wiki">Code Style Guidelines &mdash; Source</a></li>
76
-<li><a href="../../../help">Commands and Webpages &mdash; Lists of</a></li>
77
-<li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li>
78
-<li><a href="concepts.wiki">Concepts &mdash; Fossil Core</a></li>
79
-<li><a href="cgi.wiki">Configuration Options &mdash; CGI Script</a></li>
80
-<li><a href="server/">Configure A Fossil Server &mdash; How To</a></li>
81
-<li><a href="rebaseharm.md">Considered Harmful &mdash; Rebase</a></li>
82
-<li><a href="contact.md">Contact Information &mdash; Developer</a></li>
83
-<li><a href="shunning.wiki">Content From Fossil &mdash; Shunning: Deleting</a></li>
84
-<li><a href="defcsp.md">Content Security Policy &mdash; The Default</a></li>
85
-<li><a href="contribute.wiki"><b>Contributing Code or Documentation To The Fossil Project</b></a></li>
86
-<li><a href="copyright-release.html"><b>Contributor License Agreement</b></a></li>
87
-<li><a href="whyusefossil.wiki">Control &mdash; Benefits Of Version</a></li>
88
-<li><a href="concepts.wiki">Core Concepts &mdash; Fossil</a></li>
89
-<li><a href="newrepo.wiki">Create A New Fossil Repository &mdash; How To</a></li>
90
-<li><a href="private.wiki"><b>Creating, Syncing, and Deleting Private Branches</b></a></li>
91
-<li><a href="qandc.wiki">Criticisms &mdash; Questions And</a></li>
92
-<li><a href="css-tricks.md">CSS Tips and Tricks &mdash; Fossil</a></li>
93
-<li><a href="customskin.md"><b>Custom Skins</b></a></li>
94
-<li><a href="customskin.md">Customizing The Appearance of Web Pages &mdash; Theming:</a></li>
95
-<li><a href="custom_ticket.wiki"><b>Customizing The Ticket System</b></a></li>
96
-<li><a href="customgraph.md">Customizing the Timeline Graph &mdash; Theming:</a></li>
97
-<li><a href="fossil-is-not-relational.md">Data Model &mdash; Introduction to the (Non-relational) Fossil</a></li>
98
-<li><a href="tech_overview.wiki">Databases Used By Fossil &mdash; SQLite</a></li>
99
-<li><a href="defcsp.md">Default Content Security Policy &mdash; The</a></li>
100
-<li><a href="antibot.wiki"><b>Defense against Spiders and Bots</b></a></li>
101
-<li><a href="shunning.wiki">Deleting Content From Fossil &mdash; Shunning:</a></li>
102
-<li><a href="private.wiki">Deleting Private Branches &mdash; Creating, Syncing, and</a></li>
103
-<li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm &mdash; Fossil</a></li>
104
-<li><a href="delta_format.wiki">Delta Format &mdash; Fossil</a></li>
105
-<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
106
-<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
107
-<li><a href="contact.md"><b>Developer Contact Information</b></a></li>
108
-<li><a href="hacker-howto.wiki">Developers Guide &mdash; Fossil</a></li>
109
-<li><a href="pikchr.md">Diagram Language &mdash; The Pikchr</a></li>
110
-<li><a href="caps/admin-v-setup.md"><b>Differences Between Setup and Admin Users</b></a></li>
111
-<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
112
-<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
113
-<li><a href="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
114
-<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
115
-<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
116
-<li><a href="alerts.md"><b>Email Alerts And Notifications</b></a></li>
117
-<li><a href="embeddeddoc.wiki"><b>Embedded Project Documentation</b></a></li>
118
-<li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
119
-<li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
120
-<li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
121
-<li><a href="event.wiki"><b>Events</b></a></li>
122
-<li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
123
-<li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
124
-<li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
125
-<li><a href="serverext.wiki">Extensions &mdash; CGI Server</a></li>
126
-<li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts &mdash; Adding</a></li>
127
-<li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
128
-<li><a href="fileformat.wiki">File Format &mdash; Fossil</a></li>
129
-<li><a href="globs.md"><b>File Name Glob Patterns</b></a></li>
130
-<li><a href="fileedit-page.md">fileedit Page &mdash; The</a></li>
131
-<li><a href="unvers.wiki">Files &mdash; Unversioned</a></li>
132
-<li><a href="branching.wiki">Forking, Merging, and Tagging &mdash; Branching,</a></li>
133
-<li><a href="delta_format.wiki">Format &mdash; Fossil Delta</a></li>
134
-<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
135
-<li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size &mdash; Image</a></li>
136
-<li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
137
-<li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>
138
-<li><a href="forum.wiki">Forums &mdash; Fossil</a></li>
139
-<li><a href="cap-theorem.md"><b>Fossil and the CAP Theorem</b></a></li>
140
-<li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>
141
-<li><a href="chat.md"><b>Fossil Chat</b></a></li>
142
-<li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
143
-<li><a href="css-tricks.md"><b>Fossil CSS Tips and Tricks</b></a></li>
144
-<li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
145
-<li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
146
-<li><a href="hacker-howto.wiki"><b>Fossil Developers Guide</b></a></li>
147
-<li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>
148
-<li><a href="forum.wiki"><b>Fossil Forums</b></a></li>
149
-<li><a href="grep.md"><b>Fossil grep vs POSIX grep</b></a></li>
150
-<li><a href="quickstart.wiki"><b>Fossil Quick Start Guide</b></a></li>
151
-<li><a href="selfcheck.wiki"><b>Fossil Repository Integrity Self Checks</b></a></li>
152
-<li><a href="selfhost.wiki"><b>Fossil Self Hosting Repositories</b></a></li>
153
-<li><a href="settings.wiki"><b>Fossil Settings</b></a></li>
154
-<li><a href="hints.wiki"><b>Fossil Tips And Usage Hints</b></a></li>
155
-<li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li>
156
-<li><a href="quotes.wiki">Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are Saying About</a></li>
157
-<li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li>
158
-<li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li>
159
-<li><a href="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
160
-<li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
161
-<li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
162
-<li><a href="mirrorlimitations.md">Git Mirrors &mdash; Limitations On</a></li>
163
-<li><a href="gitusers.md"><b>Git to Fossil Translation Guide</b></a></li>
164
-<li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
165
-<li><a href="mirrortogithub.md">GitHub &mdash; How To Mirror A Fossil Repository On</a></li>
166
-<li><a href="globs.md">Glob Patterns &mdash; File Name</a></li>
167
-<li><a href="env-opts.md">Global Options &mdash; Environment Variables and</a></li>
168
-<li><a href="customgraph.md">Graph &mdash; Theming: Customizing the Timeline</a></li>
169
-<li><a href="grep.md">grep &mdash; Fossil grep vs POSIX</a></li>
170
-<li><a href="grep.md">grep vs POSIX grep &mdash; Fossil</a></li>
171
-<li><a href="hacker-howto.wiki">Guide &mdash; Fossil Developers</a></li>
172
-<li><a href="quickstart.wiki">Guide &mdash; Fossil Quick Start</a></li>
173
-<li><a href="gitusers.md">Guide &mdash; Git to Fossil Translation</a></li>
174
-<li><a href="style.wiki">Guidelines &mdash; Source Code Style</a></li>
175
-<li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li>
176
-<li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li>
177
-<li><a href="rebaseharm.md">Harmful &mdash; Rebase Considered</a></li>
178
-<li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li>
179
-<li><a href="hashes.md"><b>Hashes: Fossil Artifact Identification</b></a></li>
180
-<li><a href="hints.wiki">Hints &mdash; Fossil Tips And Usage</a></li>
181
-<li><a href="history.md">History Of Fossil &mdash; The Purpose And</a></li>
182
-<li><a href="index.wiki"><b>Home Page</b></a></li>
183
-<li><a href="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
184
-<li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li>
185
-<li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li>
186
-<li><a href="server/"><b>How To Configure A Fossil Server</b></a></li>
187
-<li><a href="newrepo.wiki"><b>How To Create A New Fossil Repository</b></a></li>
188
-<li><a href="mirrortogithub.md"><b>How To Mirror A Fossil Repository On GitHub</b></a></li>
189
-<li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li>
190
-<li><a href="hacker-howto.wiki">How-To &mdash; Hacker</a></li>
191
-<li><a href="fossil-from-msvc.wiki">IDE &mdash; Integrating Fossil in the Microsoft Express 2010</a></li>
192
-<li><a href="hashes.md">Identification &mdash; Hashes: Fossil Artifact</a></li>
193
-<li><a href="image-format-vs-repo-size.md"><b>Image Format vs Fossil Repo Size</b></a></li>
194
-<li><a href="tech_overview.wiki">Implementation Of Fossil &mdash; A Technical Overview Of The Design And</a></li>
195
-<li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li>
196
-<li><a href="contact.md">Information &mdash; Developer Contact</a></li>
197
-<li><a href="build.wiki">Installing Fossil &mdash; Compiling and</a></li>
198
-<li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li>
199
-<li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
200
-<li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
201
-<li><a href="interwiki.md"><b>Interwiki Links</b></a></li>
202
-<li><a href="fossil-is-not-relational.md"><b>Introduction to the (Non-relational) Fossil Data Model</b></a></li>
203
-<li><a href="blockchain.md"><b>Is Fossil A Blockchain?</b></a></li>
204
-<li><a href="chroot.md">Jail &mdash; Server Chroot</a></li>
205
-<li><a href="javascript.md">JavaScript in Fossil &mdash; Use of</a></li>
206
-<li><a href="pikchr.md">Language &mdash; The Pikchr Diagram</a></li>
207
-<li><a href="th1.md">Language &mdash; The TH1 Scripting</a></li>
208
-<li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
209
-<li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li>
210
-<li><a href="interwiki.md">Links &mdash; Interwiki</a></li>
211
-<li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li>
212
-<li><a href="loadmgmt.md">Load &mdash; Managing Server</a></li>
213
-<li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
214
-<li><a href="loadmgmt.md"><b>Managing Server Load</b></a></li>
215
-<li><a href="../../../sitemap">Map &mdash; Site</a></li>
216
-<li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li>
217
-<li><a href="backoffice.md">mechanism of Fossil &mdash; The Backoffice</a></li>
218
-<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
219
-<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
220
-<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
221
-<li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub &mdash; How To</a></li>
222
-<li><a href="mirrorlimitations.md">Mirrors &mdash; Limitations On Git</a></li>
223
-<li><a href="fossil-is-not-relational.md">Model &mdash; Introduction to the (Non-relational) Fossil Data</a></li>
224
-<li><a href="globs.md">Name Glob Patterns &mdash; File</a></li>
225
-<li><a href="checkin_names.wiki">Names &mdash; Check-in And Version</a></li>
226
-<li><a href="adding_code.wiki">New Features To Fossil &mdash; Adding</a></li>
227
-<li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
228
-<li><a href="alerts.md">Notifications &mdash; Email Alerts And</a></li>
229
-<li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
230
-<li><a href="pop.wiki">Operation &mdash; Principles Of</a></li>
231
-<li><a href="cgi.wiki">Options &mdash; CGI Script Configuration</a></li>
232
-<li><a href="env-opts.md">Options &mdash; Environment Variables and Global</a></li>
233
-<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
234
-<li><a href="index.wiki">Page &mdash; Home</a></li>
235
-<li><a href="fileedit-page.md">Page &mdash; The fileedit</a></li>
236
-<li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
237
-<li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>
238
-<li><a href="password.wiki"><b>Password Management And Authentication</b></a></li>
239
-<li><a href="globs.md">Patterns &mdash; File Name Glob</a></li>
240
-<li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What</a></li>
241
-<li><a href="stats.wiki"><b>Performance Statistics</b></a></li>
242
-<li><a href="pikchr.md">Pikchr Diagram Language &mdash; The</a></li>
243
-<li><a href="defcsp.md">Policy &mdash; The Default Content Security</a></li>
244
-<li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 &mdash; Hash</a></li>
245
-<li><a href="grep.md">POSIX grep &mdash; Fossil grep vs</a></li>
246
-<li><a href="../test/release-checklist.wiki"><b>Pre-Release Testing Checklist</b></a></li>
247
-<li><a href="pop.wiki"><b>Principles Of Operation</b></a></li>
248
-<li><a href="private.wiki">Private Branches &mdash; Creating, Syncing, and Deleting</a></li>
249
-<li><a href="makefile.wiki">Process &mdash; The Fossil Build</a></li>
250
-<li><a href="contribute.wiki">Project &mdash; Contributing Code or Documentation To The Fossil</a></li>
251
-<li><a href="embeddeddoc.wiki">Project Documentation &mdash; Embedded</a></li>
252
-<li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
253
-<li><a href="childprojects.wiki">Projects &mdash; Child</a></li>
254
-<li><a href="fossil_prompt.wiki">Prompt &mdash; Fossilized Bash</a></li>
255
-<li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
256
-<li><a href="history.md">Purpose And History Of Fossil &mdash; The</a></li>
257
-<li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
258
-<li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li>
259
-<li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
260
-<li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li>
261
-<li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li>
262
-<li><a href="caps/ref.html">Reference &mdash; User Capability</a></li>
263
-<li><a href="backup.md">Remote Fossil Repository &mdash; Backing Up a</a></li>
264
-<li><a href="image-format-vs-repo-size.md">Repo Size &mdash; Image Format vs Fossil</a></li>
265
-<li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</a></li>
266
-<li><a href="encryptedrepos.wiki">Repositories &mdash; How To Use Encrypted</a></li>
267
-<li><a href="backup.md">Repository &mdash; Backing Up a Remote Fossil</a></li>
268
-<li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
269
-<li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
270
-<li><a href="mirrortogithub.md">Repository On GitHub &mdash; How To Mirror A Fossil</a></li>
271
-<li><a href="reviews.wiki"><b>Reviews</b></a></li>
272
-<li><a href="../../../md_rules">Rules &mdash; Markdown Formatting</a></li>
273
-<li><a href="../../../wiki_rules">Rules &mdash; Wiki Formatting</a></li>
274
-<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User &mdash; Up and</a></li>
275
-<li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
276
-<li><a href="cgi.wiki">Script Configuration Options &mdash; CGI</a></li>
277
-<li><a href="th1.md">Scripting Language &mdash; The TH1</a></li>
278
-<li><a href="serverext.wiki">Scripts &mdash; Adding Extensions To A Fossil Server Using CGI</a></li>
279
-<li><a href="defcsp.md">Security Policy &mdash; The Default Content</a></li>
280
-<li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
281
-<li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
282
-<li><a href="server/">Server &mdash; How To Configure A Fossil</a></li>
283
-<li><a href="chroot.md"><b>Server Chroot Jail</b></a></li>
284
-<li><a href="serverext.wiki">Server Extensions &mdash; CGI</a></li>
285
-<li><a href="loadmgmt.md">Server Load &mdash; Managing</a></li>
286
-<li><a href="serverext.wiki">Server Using CGI Scripts &mdash; Adding Extensions To A Fossil</a></li>
287
-<li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
288
-<li><a href="caps/admin-v-setup.md">Setup and Admin Users &mdash; Differences Between</a></li>
289
-<li><a href="hashpolicy.wiki">SHA1 and SHA3-256 &mdash; Hash Policy: Choosing Between</a></li>
290
-<li><a href="hashpolicy.wiki">SHA3-256 &mdash; Hash Policy: Choosing Between SHA1 and</a></li>
291
-<li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li>
292
-<li><a href="fiveminutes.wiki">Single User &mdash; Up and Running in 5 Minutes as a</a></li>
293
-<li><a href="../../../sitemap"><b>Site Map</b></a></li>
294
-<li><a href="image-format-vs-repo-size.md">Size &mdash; Image Format vs Fossil Repo</a></li>
295
-<li><a href="customskin.md">Skins &mdash; Custom</a></li>
296
-<li><a href="style.wiki"><b>Source Code Style Guidelines</b></a></li>
297
-<li><a href="antibot.wiki">Spiders and Bots &mdash; Defense against</a></li>
298
-<li><a href="tech_overview.wiki"><b>SQLite Databases Used By Fossil</b></a></li>
299
-<li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
300
-<li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
301
-<li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
302
-<li><a href="style.wiki">Style Guidelines &mdash; Source Code</a></li>
303
-<li><a href="foss-cklist.wiki">Successful Open-Source Projects &mdash; Checklist For</a></li>
304
-<li><a href="sync.wiki">Sync Protocol &mdash; The Fossil</a></li>
305
-<li><a href="private.wiki">Syncing, and Deleting Private Branches &mdash; Creating,</a></li>
306
-<li><a href="custom_ticket.wiki">System &mdash; Customizing The Ticket</a></li>
307
-<li><a href="tickets.wiki">System &mdash; The Fossil Ticket</a></li>
308
-<li><a href="branching.wiki">Tagging &mdash; Branching, Forking, Merging, and</a></li>
309
-<li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil &mdash; A</a></li>
310
-<li><a href="../test/release-checklist.wiki">Testing Checklist &mdash; Pre-Release</a></li>
311
-<li><a href="th1.md">TH1 Scripting Language &mdash; The</a></li>
312
-<li><a href="backoffice.md"><b>The "Backoffice" mechanism of Fossil</b></a></li>
313
-<li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li>
314
-<li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
315
-<li><a href="fileedit-page.md"><b>The fileedit Page</b></a></li>
316
-<li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
317
-<li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
318
-<li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
319
-<li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
320
-<li><a href="pikchr.md"><b>The Pikchr Diagram Language</b></a></li>
321
-<li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li>
322
-<li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
323
-<li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
324
-<li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
325
-<li><a href="cap-theorem.md">Theorem &mdash; Fossil and the CAP</a></li>
326
-<li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
327
-<li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
328
-<li><a href="tickets.wiki">Ticket System &mdash; The Fossil</a></li>
329
-<li><a href="customgraph.md">Timeline Graph &mdash; Theming: Customizing the</a></li>
330
-<li><a href="css-tricks.md">Tips and Tricks &mdash; Fossil CSS</a></li>
331
-<li><a href="hints.wiki">Tips And Usage Hints &mdash; Fossil</a></li>
332
-<li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
333
-<li><a href="gitusers.md">Translation Guide &mdash; Git to Fossil</a></li>
334
-<li><a href="css-tricks.md">Tricks &mdash; Fossil CSS Tips and</a></li>
335
-<li><a href="unvers.wiki"><b>Unversioned Files</b></a></li>
336
-<li><a href="backup.md">Up a Remote Fossil Repository &mdash; Backing</a></li>
337
-<li><a href="fiveminutes.wiki"><b>Up and Running in 5 Minutes as a Single User</b></a></li>
338
-<li><a href="co-vs-up.md">Update &mdash; Checkout vs</a></li>
339
-<li><a href="hints.wiki">Usage Hints &mdash; Fossil Tips And</a></li>
340
-<li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
341
-<li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
342
-<li><a href="caps/">User Capabilities &mdash; Administering</a></li>
343
-<li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
344
-<li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
345
-<li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
346
-<li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
347
-<li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
348
-<li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
349
-<li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
350
-<li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
351
-<li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size &mdash; Image Format</a></li>
352
-<li><a href="grep.md">vs POSIX grep &mdash; Fossil grep</a></li>
353
-<li><a href="co-vs-up.md">vs Update &mdash; Checkout</a></li>
354
-<li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
355
-<li><a href="customskin.md">Web Pages &mdash; Theming: Customizing The Appearance of</a></li>
356
-<li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li>
357
-<li><a href="../../../help">Webpages &mdash; Lists of Commands and</a></li>
358
-<li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
359
-<li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li>
360
-<li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li>
361
-<li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></a></li>
362
-<li><a href="ckout-workflows.md">Workflows &mdash; Check-Out</a></li>
363
-<li><a href="aboutdownload.wiki">Works &mdash; How The Download Page</a></li>
364
-<li><a href="aboutcgi.wiki">Works In Fossil &mdash; How CGI</a></li>
365
-<li><a href="whyusefossil.wiki">You Should Use Fossil &mdash; Why</a></li>
25
+<li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li>
26
+<li><a href="serverext.wiki">Adding Extensions To A Fossil Server Using CGI Scripts</a></li>
27
+<li><a href="adding_code.wiki">Adding New Features To Fossil</a></li>
28
+<li><a href="caps/">Administering User Capabilities</a></li>
29
+<li><a href="backup.md">Backing Up a Remote Fossil Repository</a></li>
30
+<li><a href="whyusefossil.wiki">Benefits Of Version Control</a></li>
31
+<li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
32
+<li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
33
+<li><a href="cgi.wiki">CGI Script Configuration Options</a></li>
34
+<li><a href="serverext.wiki">CGI Server Extensions</a></li>
35
+<li><a href="checkin_names.wiki">Check-in And Version Names</a></li>
36
+<li><a href="checkin.wiki">Check-in Checklist</a></li>
37
+<li><a href="ckout-workflows.md">Check-Out Workflows</a></li>
38
+<li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li>
39
+<li><a href="co-vs-up.md">Checkout vs Update</a></li>
40
+<li><a href="childprojects.wiki">Child Projects</a></li>
41
+<li><a href="build.wiki">Compiling and Installing Fossil</a></li>
42
+<li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li>
43
+<li><a href="copyright-release.html">Contributor License Agreement</a></li>
44
+<li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li>
45
+<li><a href="customskin.md">Custom Skins</a></li>
46
+<li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li>
47
+<li><a href="antibot.wiki">Defense against Spiders and Bots</a></li>
48
+<li><a href="delta-manifests.md">Delta Manifests</a></li>
49
+<li><a href="contact.md">Developer Contact Information</a></li>
50
+<li><a href="caps/admin-v-setup.md">Differences Between Setup and Admin Users</a></li>
51
+<li><a href="alerts.md">Email Alerts And Notifications</a></li>
52
+<li><a href="embeddeddoc.wiki">Embedded Project Documentation</a></li>
53
+<li><a href="env-opts.md">Environment Variables and Global Options</a></li>
54
+<li><a href="event.wiki">Events</a></li>
55
+<li><a href="globs.md">File Name Glob Patterns</a></li>
56
+<li><a href="cap-theorem.md">Fossil and the CAP Theorem</a></li>
57
+<li><a href="changes.wiki">Fossil Changelog</a></li>
58
+<li><a href="chat.md">Fossil Chat</a></li>
59
+<li><a href="concepts.wiki">Fossil Core Concepts</a></li>
60
+<li><a href="css-tricks.md">Fossil CSS Tips and Tricks</a></li>
61
+<li><a href="delta_encoder_algorithm.wiki">Fossil Delta Encoding Algorithm</a></li>
62
+<li><a href="delta_format.wiki">Fossil Delta Format</a></li>
63
+<li><a href="hacker-howto.wiki">Fossil Developers Guide</a></li>
64
+<li><a href="fileformat.wiki">Fossil File Format</a></li>
65
+<li><a href="forum.wiki">Fossil Forums</a></li>
66
+<li><a href="grep.md">Fossil grep vs POSIX grep</a></li>
67
+<li><a href="quickstart.wiki">Fossil Quick Start Guide</a></li>
68
+<li><a href="selfcheck.wiki">Fossil Repository Integrity Self Checks</a></li>
69
+<li><a href="selfhost.wiki">Fossil Self Hosting Repositories</a></li>
70
+<li><a href="settings.wiki">Fossil Settings</a></li>
71
+<li><a href="hints.wiki">Fossil Tips And Usage Hints</a></li>
72
+<li><a href="fossil-v-git.wiki">Fossil Versus Git</a></li>
73
+<li><a href="fossil_prompt.wiki">Fossilized Bash Prompt</a></li>
74
+<li><a href="faq.wiki">Frequently Asked Questions</a></li>
75
+<li><a href="gitusers.md">Git to Fossil Translation Guide</a></li>
76
+<li><a href="hacker-howto.wiki">Hacker How-To</a></li>
77
+<li><a href="adding_code.wiki">Hacking Fossil</a></li>
78
+<li><a href="hashpolicy.wiki">Hash Policy: Choosing Between SHA1 and SHA3-256</a></li>
79
+<li><a href="hashes.md">Hashes: Fossil Artifact Identification</a></li>
80
+<li><a href="index.wiki">Home Page</a></li>
81
+<li><a href="aboutcgi.wiki">How CGI Works In Fossil</a></li>
82
+<li><a href="aboutdownload.wiki">How The Download Page Works</a></li>
83
+<li><a href="server/">How To Configure A Fossil Server</a></li>
84
+<li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li>
85
+<li><a href="mirrortogithub.md">How To Mirror A Fossil Repository On GitHub</a></li>
86
+<li><a href="encryptedrepos.wiki">How To Use Encrypted Repositories</a></li>
87
+<li><a href="image-format-vs-repo-size.md">Image Format vs Fossil Repo Size</a></li>
88
+<li><a href="inout.wiki">Import And Export To And From Git</a></li>
89
+<li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li>
90
+<li><a href="interwiki.md">Interwiki Links</a></li>
91
+<li><a href="fossil-is-not-relational.md">Introduction to the (Non-relational) Fossil Data Model</a></li>
92
+<li><a href="blockchain.md">Is Fossil A Blockchain?</a></li>
93
+<li><a href="mirrorlimitations.md">Limitations On Git Mirrors</a></li>
94
+<li><a href="../../../help">Lists of Commands and Webpages</a></li>
95
+<li><a href="loadmgmt.md">Managing Server Load</a></li>
96
+<li><a href="../../../md_rules">Markdown Formatting Rules</a></li>
97
+<li><a href="password.wiki">Password Management And Authentication</a></li>
98
+<li><a href="stats.wiki">Performance Statistics</a></li>
99
+<li><a href="../test/release-checklist.wiki">Pre-Release Testing Checklist</a></li>
100
+<li><a href="pop.wiki">Principles Of Operation</a></li>
101
+<li><a href="qandc.wiki">Questions And Criticisms</a></li>
102
+<li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li>
103
+<li><a href="rebaseharm.md">Rebase Considered Harmful</a></li>
104
+<li><a href="reviews.wiki">Reviews</a></li>
105
+<li><a href="chroot.md">Server Chroot Jail</a></li>
106
+<li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li>
107
+<li><a href="../../../sitemap">Site Map</a></li>
108
+<li><a href="style.wiki">Source Code Style Guidelines</a></li>
109
+<li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
110
+<li><a href="backoffice.md">The "Backoffice" mechanism of Fossil</a></li>
111
+<li><a href="blame.wiki">The Annotate/Blame Algorithm Of Fossil</a></li>
112
+<li><a href="defcsp.md">The Default Content Security Policy</a></li>
113
+<li><a href="fileedit-page.md">The fileedit Page</a></li>
114
+<li><a href="makefile.wiki">The Fossil Build Process</a></li>
115
+<li><a href="sync.wiki">The Fossil Sync Protocol</a></li>
116
+<li><a href="tickets.wiki">The Fossil Ticket System</a></li>
117
+<li><a href="webui.wiki">The Fossil Web Interface</a></li>
118
+<li><a href="pikchr.md">The Pikchr Diagram Language</a></li>
119
+<li><a href="history.md">The Purpose And History Of Fossil</a></li>
120
+<li><a href="th1.md">The TH1 Scripting Language</a></li>
121
+<li><a href="customskin.md">Theming: Customizing The Appearance of Web Pages</a></li>
122
+<li><a href="customgraph.md">Theming: Customizing the Timeline Graph</a></li>
123
+<li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li>
124
+<li><a href="unvers.wiki">Unversioned Files</a></li>
125
+<li><a href="fiveminutes.wiki">Up and Running in 5 Minutes as a Single User</a></li>
126
+<li><a href="javascript.md">Use of JavaScript in Fossil</a></li>
127
+<li><a href="caps/ref.html">User Capability Reference</a></li>
128
+<li><a href="ssl.wiki">Using SSL with Fossil</a></li>
129
+<li><a href="webpage-ex.md">Webpage Examples</a></li>
130
+<li><a href="whyusefossil.wiki">Why You Should Use Fossil</a></li>
131
+<li><a href="../../../wiki_rules">Wiki Formatting Rules</a></li>
132
+<li><a href="wikitheory.wiki">Wiki In Fossil</a></li>
366133
</ul></div>
367134
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -18,349 +18,116 @@
18 <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
19 <li> <a href='http://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 <ul>
25 <li><a href="fossil-is-not-relational.md">(Non-relational) Fossil Data Model &mdash; Introduction to the</a></li>
26 <li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
27 <li><a href="fossil-from-msvc.wiki">2010 IDE &mdash; Integrating Fossil in the Microsoft Express</a></li>
28 <li><a href="tech_overview.wiki"><b>A Technical Overview Of The Design And Implementation Of Fossil</b></a></li>
29 <li><a href="serverext.wiki"><b>Adding Extensions To A Fossil Server Using CGI Scripts</b></a></li>
30 <li><a href="adding_code.wiki"><b>Adding New Features To Fossil</b></a></li>
31 <li><a href="caps/admin-v-setup.md">Admin Users &mdash; Differences Between Setup and</a></li>
32 <li><a href="caps/"><b>Administering User Capabilities</b></a></li>
33 <li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
34 <li><a href="alerts.md">Alerts And Notifications &mdash; Email</a></li>
35 <li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
36 <li><a href="blame.wiki">Algorithm Of Fossil &mdash; The Annotate/Blame</a></li>
37 <li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil &mdash; The</a></li>
38 <li><a href="customskin.md">Appearance of Web Pages &mdash; Theming: Customizing The</a></li>
39 <li><a href="hashes.md">Artifact Identification &mdash; Hashes: Fossil</a></li>
40 <li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
41 <li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
42 <li><a href="backup.md"><b>Backing Up a Remote Fossil Repository</b></a></li>
43 <li><a href="backoffice.md">Backoffice mechanism of Fossil &mdash; The</a></li>
44 <li><a href="fossil_prompt.wiki">Bash Prompt &mdash; Fossilized</a></li>
45 <li><a href="whyusefossil.wiki"><b>Benefits Of Version Control</b></a></li>
46 <li><a href="caps/admin-v-setup.md">Between Setup and Admin Users &mdash; Differences</a></li>
47 <li><a href="hashpolicy.wiki">Between SHA1 and SHA3-256 &mdash; Hash Policy: Choosing</a></li>
48 <li><a href="blockchain.md">Blockchain? &mdash; Is Fossil A</a></li>
49 <li><a href="antibot.wiki">Bots &mdash; Defense against Spiders and</a></li>
50 <li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
51 <li><a href="branching.wiki"><b>Branching, Forking, Merging, and Tagging</b></a></li>
52 <li><a href="bugtheory.wiki"><b>Bug Tracking In Fossil</b></a></li>
53 <li><a href="makefile.wiki">Build Process &mdash; The Fossil</a></li>
54 <li><a href="cap-theorem.md">CAP Theorem &mdash; Fossil and the</a></li>
55 <li><a href="caps/">Capabilities &mdash; Administering User</a></li>
56 <li><a href="caps/ref.html">Capability Reference &mdash; User</a></li>
57 <li><a href="cgi.wiki"><b>CGI Script Configuration Options</b></a></li>
58 <li><a href="serverext.wiki">CGI Scripts &mdash; Adding Extensions To A Fossil Server Using</a></li>
59 <li><a href="serverext.wiki"><b>CGI Server Extensions</b></a></li>
60 <li><a href="aboutcgi.wiki">CGI Works In Fossil &mdash; How</a></li>
61 <li><a href="changes.wiki">Changelog &mdash; Fossil</a></li>
62 <li><a href="chat.md">Chat &mdash; Fossil</a></li>
63 <li><a href="checkin_names.wiki"><b>Check-in And Version Names</b></a></li>
64 <li><a href="checkin.wiki"><b>Check-in Checklist</b></a></li>
65 <li><a href="ckout-workflows.md"><b>Check-Out Workflows</b></a></li>
66 <li><a href="checkin.wiki">Checklist &mdash; Check-in</a></li>
67 <li><a href="../test/release-checklist.wiki">Checklist &mdash; Pre-Release Testing</a></li>
68 <li><a href="foss-cklist.wiki"><b>Checklist For Successful Open-Source Projects</b></a></li>
69 <li><a href="co-vs-up.md"><b>Checkout vs Update</b></a></li>
70 <li><a href="selfcheck.wiki">Checks &mdash; Fossil Repository Integrity Self</a></li>
71 <li><a href="childprojects.wiki"><b>Child Projects</b></a></li>
72 <li><a href="hashpolicy.wiki">Choosing Between SHA1 and SHA3-256 &mdash; Hash Policy:</a></li>
73 <li><a href="chroot.md">Chroot Jail &mdash; Server</a></li>
74 <li><a href="contribute.wiki">Code or Documentation To The Fossil Project &mdash; Contributing</a></li>
75 <li><a href="style.wiki">Code Style Guidelines &mdash; Source</a></li>
76 <li><a href="../../../help">Commands and Webpages &mdash; Lists of</a></li>
77 <li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li>
78 <li><a href="concepts.wiki">Concepts &mdash; Fossil Core</a></li>
79 <li><a href="cgi.wiki">Configuration Options &mdash; CGI Script</a></li>
80 <li><a href="server/">Configure A Fossil Server &mdash; How To</a></li>
81 <li><a href="rebaseharm.md">Considered Harmful &mdash; Rebase</a></li>
82 <li><a href="contact.md">Contact Information &mdash; Developer</a></li>
83 <li><a href="shunning.wiki">Content From Fossil &mdash; Shunning: Deleting</a></li>
84 <li><a href="defcsp.md">Content Security Policy &mdash; The Default</a></li>
85 <li><a href="contribute.wiki"><b>Contributing Code or Documentation To The Fossil Project</b></a></li>
86 <li><a href="copyright-release.html"><b>Contributor License Agreement</b></a></li>
87 <li><a href="whyusefossil.wiki">Control &mdash; Benefits Of Version</a></li>
88 <li><a href="concepts.wiki">Core Concepts &mdash; Fossil</a></li>
89 <li><a href="newrepo.wiki">Create A New Fossil Repository &mdash; How To</a></li>
90 <li><a href="private.wiki"><b>Creating, Syncing, and Deleting Private Branches</b></a></li>
91 <li><a href="qandc.wiki">Criticisms &mdash; Questions And</a></li>
92 <li><a href="css-tricks.md">CSS Tips and Tricks &mdash; Fossil</a></li>
93 <li><a href="customskin.md"><b>Custom Skins</b></a></li>
94 <li><a href="customskin.md">Customizing The Appearance of Web Pages &mdash; Theming:</a></li>
95 <li><a href="custom_ticket.wiki"><b>Customizing The Ticket System</b></a></li>
96 <li><a href="customgraph.md">Customizing the Timeline Graph &mdash; Theming:</a></li>
97 <li><a href="fossil-is-not-relational.md">Data Model &mdash; Introduction to the (Non-relational) Fossil</a></li>
98 <li><a href="tech_overview.wiki">Databases Used By Fossil &mdash; SQLite</a></li>
99 <li><a href="defcsp.md">Default Content Security Policy &mdash; The</a></li>
100 <li><a href="antibot.wiki"><b>Defense against Spiders and Bots</b></a></li>
101 <li><a href="shunning.wiki">Deleting Content From Fossil &mdash; Shunning:</a></li>
102 <li><a href="private.wiki">Deleting Private Branches &mdash; Creating, Syncing, and</a></li>
103 <li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm &mdash; Fossil</a></li>
104 <li><a href="delta_format.wiki">Delta Format &mdash; Fossil</a></li>
105 <li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
106 <li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
107 <li><a href="contact.md"><b>Developer Contact Information</b></a></li>
108 <li><a href="hacker-howto.wiki">Developers Guide &mdash; Fossil</a></li>
109 <li><a href="pikchr.md">Diagram Language &mdash; The Pikchr</a></li>
110 <li><a href="caps/admin-v-setup.md"><b>Differences Between Setup and Admin Users</b></a></li>
111 <li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
112 <li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
113 <li><a href="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
114 <li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
115 <li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
116 <li><a href="alerts.md"><b>Email Alerts And Notifications</b></a></li>
117 <li><a href="embeddeddoc.wiki"><b>Embedded Project Documentation</b></a></li>
118 <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
119 <li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
120 <li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
121 <li><a href="event.wiki"><b>Events</b></a></li>
122 <li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
123 <li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
124 <li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
125 <li><a href="serverext.wiki">Extensions &mdash; CGI Server</a></li>
126 <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts &mdash; Adding</a></li>
127 <li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
128 <li><a href="fileformat.wiki">File Format &mdash; Fossil</a></li>
129 <li><a href="globs.md"><b>File Name Glob Patterns</b></a></li>
130 <li><a href="fileedit-page.md">fileedit Page &mdash; The</a></li>
131 <li><a href="unvers.wiki">Files &mdash; Unversioned</a></li>
132 <li><a href="branching.wiki">Forking, Merging, and Tagging &mdash; Branching,</a></li>
133 <li><a href="delta_format.wiki">Format &mdash; Fossil Delta</a></li>
134 <li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
135 <li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size &mdash; Image</a></li>
136 <li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
137 <li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>
138 <li><a href="forum.wiki">Forums &mdash; Fossil</a></li>
139 <li><a href="cap-theorem.md"><b>Fossil and the CAP Theorem</b></a></li>
140 <li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>
141 <li><a href="chat.md"><b>Fossil Chat</b></a></li>
142 <li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
143 <li><a href="css-tricks.md"><b>Fossil CSS Tips and Tricks</b></a></li>
144 <li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
145 <li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
146 <li><a href="hacker-howto.wiki"><b>Fossil Developers Guide</b></a></li>
147 <li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>
148 <li><a href="forum.wiki"><b>Fossil Forums</b></a></li>
149 <li><a href="grep.md"><b>Fossil grep vs POSIX grep</b></a></li>
150 <li><a href="quickstart.wiki"><b>Fossil Quick Start Guide</b></a></li>
151 <li><a href="selfcheck.wiki"><b>Fossil Repository Integrity Self Checks</b></a></li>
152 <li><a href="selfhost.wiki"><b>Fossil Self Hosting Repositories</b></a></li>
153 <li><a href="settings.wiki"><b>Fossil Settings</b></a></li>
154 <li><a href="hints.wiki"><b>Fossil Tips And Usage Hints</b></a></li>
155 <li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li>
156 <li><a href="quotes.wiki">Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are Saying About</a></li>
157 <li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li>
158 <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li>
159 <li><a href="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
160 <li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
161 <li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
162 <li><a href="mirrorlimitations.md">Git Mirrors &mdash; Limitations On</a></li>
163 <li><a href="gitusers.md"><b>Git to Fossil Translation Guide</b></a></li>
164 <li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
165 <li><a href="mirrortogithub.md">GitHub &mdash; How To Mirror A Fossil Repository On</a></li>
166 <li><a href="globs.md">Glob Patterns &mdash; File Name</a></li>
167 <li><a href="env-opts.md">Global Options &mdash; Environment Variables and</a></li>
168 <li><a href="customgraph.md">Graph &mdash; Theming: Customizing the Timeline</a></li>
169 <li><a href="grep.md">grep &mdash; Fossil grep vs POSIX</a></li>
170 <li><a href="grep.md">grep vs POSIX grep &mdash; Fossil</a></li>
171 <li><a href="hacker-howto.wiki">Guide &mdash; Fossil Developers</a></li>
172 <li><a href="quickstart.wiki">Guide &mdash; Fossil Quick Start</a></li>
173 <li><a href="gitusers.md">Guide &mdash; Git to Fossil Translation</a></li>
174 <li><a href="style.wiki">Guidelines &mdash; Source Code Style</a></li>
175 <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li>
176 <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li>
177 <li><a href="rebaseharm.md">Harmful &mdash; Rebase Considered</a></li>
178 <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li>
179 <li><a href="hashes.md"><b>Hashes: Fossil Artifact Identification</b></a></li>
180 <li><a href="hints.wiki">Hints &mdash; Fossil Tips And Usage</a></li>
181 <li><a href="history.md">History Of Fossil &mdash; The Purpose And</a></li>
182 <li><a href="index.wiki"><b>Home Page</b></a></li>
183 <li><a href="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
184 <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li>
185 <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li>
186 <li><a href="server/"><b>How To Configure A Fossil Server</b></a></li>
187 <li><a href="newrepo.wiki"><b>How To Create A New Fossil Repository</b></a></li>
188 <li><a href="mirrortogithub.md"><b>How To Mirror A Fossil Repository On GitHub</b></a></li>
189 <li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li>
190 <li><a href="hacker-howto.wiki">How-To &mdash; Hacker</a></li>
191 <li><a href="fossil-from-msvc.wiki">IDE &mdash; Integrating Fossil in the Microsoft Express 2010</a></li>
192 <li><a href="hashes.md">Identification &mdash; Hashes: Fossil Artifact</a></li>
193 <li><a href="image-format-vs-repo-size.md"><b>Image Format vs Fossil Repo Size</b></a></li>
194 <li><a href="tech_overview.wiki">Implementation Of Fossil &mdash; A Technical Overview Of The Design And</a></li>
195 <li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li>
196 <li><a href="contact.md">Information &mdash; Developer Contact</a></li>
197 <li><a href="build.wiki">Installing Fossil &mdash; Compiling and</a></li>
198 <li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li>
199 <li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
200 <li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
201 <li><a href="interwiki.md"><b>Interwiki Links</b></a></li>
202 <li><a href="fossil-is-not-relational.md"><b>Introduction to the (Non-relational) Fossil Data Model</b></a></li>
203 <li><a href="blockchain.md"><b>Is Fossil A Blockchain?</b></a></li>
204 <li><a href="chroot.md">Jail &mdash; Server Chroot</a></li>
205 <li><a href="javascript.md">JavaScript in Fossil &mdash; Use of</a></li>
206 <li><a href="pikchr.md">Language &mdash; The Pikchr Diagram</a></li>
207 <li><a href="th1.md">Language &mdash; The TH1 Scripting</a></li>
208 <li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
209 <li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li>
210 <li><a href="interwiki.md">Links &mdash; Interwiki</a></li>
211 <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li>
212 <li><a href="loadmgmt.md">Load &mdash; Managing Server</a></li>
213 <li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
214 <li><a href="loadmgmt.md"><b>Managing Server Load</b></a></li>
215 <li><a href="../../../sitemap">Map &mdash; Site</a></li>
216 <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li>
217 <li><a href="backoffice.md">mechanism of Fossil &mdash; The Backoffice</a></li>
218 <li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
219 <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
220 <li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
221 <li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub &mdash; How To</a></li>
222 <li><a href="mirrorlimitations.md">Mirrors &mdash; Limitations On Git</a></li>
223 <li><a href="fossil-is-not-relational.md">Model &mdash; Introduction to the (Non-relational) Fossil Data</a></li>
224 <li><a href="globs.md">Name Glob Patterns &mdash; File</a></li>
225 <li><a href="checkin_names.wiki">Names &mdash; Check-in And Version</a></li>
226 <li><a href="adding_code.wiki">New Features To Fossil &mdash; Adding</a></li>
227 <li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
228 <li><a href="alerts.md">Notifications &mdash; Email Alerts And</a></li>
229 <li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
230 <li><a href="pop.wiki">Operation &mdash; Principles Of</a></li>
231 <li><a href="cgi.wiki">Options &mdash; CGI Script Configuration</a></li>
232 <li><a href="env-opts.md">Options &mdash; Environment Variables and Global</a></li>
233 <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
234 <li><a href="index.wiki">Page &mdash; Home</a></li>
235 <li><a href="fileedit-page.md">Page &mdash; The fileedit</a></li>
236 <li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
237 <li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>
238 <li><a href="password.wiki"><b>Password Management And Authentication</b></a></li>
239 <li><a href="globs.md">Patterns &mdash; File Name Glob</a></li>
240 <li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What</a></li>
241 <li><a href="stats.wiki"><b>Performance Statistics</b></a></li>
242 <li><a href="pikchr.md">Pikchr Diagram Language &mdash; The</a></li>
243 <li><a href="defcsp.md">Policy &mdash; The Default Content Security</a></li>
244 <li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 &mdash; Hash</a></li>
245 <li><a href="grep.md">POSIX grep &mdash; Fossil grep vs</a></li>
246 <li><a href="../test/release-checklist.wiki"><b>Pre-Release Testing Checklist</b></a></li>
247 <li><a href="pop.wiki"><b>Principles Of Operation</b></a></li>
248 <li><a href="private.wiki">Private Branches &mdash; Creating, Syncing, and Deleting</a></li>
249 <li><a href="makefile.wiki">Process &mdash; The Fossil Build</a></li>
250 <li><a href="contribute.wiki">Project &mdash; Contributing Code or Documentation To The Fossil</a></li>
251 <li><a href="embeddeddoc.wiki">Project Documentation &mdash; Embedded</a></li>
252 <li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
253 <li><a href="childprojects.wiki">Projects &mdash; Child</a></li>
254 <li><a href="fossil_prompt.wiki">Prompt &mdash; Fossilized Bash</a></li>
255 <li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
256 <li><a href="history.md">Purpose And History Of Fossil &mdash; The</a></li>
257 <li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
258 <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li>
259 <li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
260 <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li>
261 <li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li>
262 <li><a href="caps/ref.html">Reference &mdash; User Capability</a></li>
263 <li><a href="backup.md">Remote Fossil Repository &mdash; Backing Up a</a></li>
264 <li><a href="image-format-vs-repo-size.md">Repo Size &mdash; Image Format vs Fossil</a></li>
265 <li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</a></li>
266 <li><a href="encryptedrepos.wiki">Repositories &mdash; How To Use Encrypted</a></li>
267 <li><a href="backup.md">Repository &mdash; Backing Up a Remote Fossil</a></li>
268 <li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
269 <li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
270 <li><a href="mirrortogithub.md">Repository On GitHub &mdash; How To Mirror A Fossil</a></li>
271 <li><a href="reviews.wiki"><b>Reviews</b></a></li>
272 <li><a href="../../../md_rules">Rules &mdash; Markdown Formatting</a></li>
273 <li><a href="../../../wiki_rules">Rules &mdash; Wiki Formatting</a></li>
274 <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User &mdash; Up and</a></li>
275 <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
276 <li><a href="cgi.wiki">Script Configuration Options &mdash; CGI</a></li>
277 <li><a href="th1.md">Scripting Language &mdash; The TH1</a></li>
278 <li><a href="serverext.wiki">Scripts &mdash; Adding Extensions To A Fossil Server Using CGI</a></li>
279 <li><a href="defcsp.md">Security Policy &mdash; The Default Content</a></li>
280 <li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
281 <li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
282 <li><a href="server/">Server &mdash; How To Configure A Fossil</a></li>
283 <li><a href="chroot.md"><b>Server Chroot Jail</b></a></li>
284 <li><a href="serverext.wiki">Server Extensions &mdash; CGI</a></li>
285 <li><a href="loadmgmt.md">Server Load &mdash; Managing</a></li>
286 <li><a href="serverext.wiki">Server Using CGI Scripts &mdash; Adding Extensions To A Fossil</a></li>
287 <li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
288 <li><a href="caps/admin-v-setup.md">Setup and Admin Users &mdash; Differences Between</a></li>
289 <li><a href="hashpolicy.wiki">SHA1 and SHA3-256 &mdash; Hash Policy: Choosing Between</a></li>
290 <li><a href="hashpolicy.wiki">SHA3-256 &mdash; Hash Policy: Choosing Between SHA1 and</a></li>
291 <li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li>
292 <li><a href="fiveminutes.wiki">Single User &mdash; Up and Running in 5 Minutes as a</a></li>
293 <li><a href="../../../sitemap"><b>Site Map</b></a></li>
294 <li><a href="image-format-vs-repo-size.md">Size &mdash; Image Format vs Fossil Repo</a></li>
295 <li><a href="customskin.md">Skins &mdash; Custom</a></li>
296 <li><a href="style.wiki"><b>Source Code Style Guidelines</b></a></li>
297 <li><a href="antibot.wiki">Spiders and Bots &mdash; Defense against</a></li>
298 <li><a href="tech_overview.wiki"><b>SQLite Databases Used By Fossil</b></a></li>
299 <li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
300 <li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
301 <li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
302 <li><a href="style.wiki">Style Guidelines &mdash; Source Code</a></li>
303 <li><a href="foss-cklist.wiki">Successful Open-Source Projects &mdash; Checklist For</a></li>
304 <li><a href="sync.wiki">Sync Protocol &mdash; The Fossil</a></li>
305 <li><a href="private.wiki">Syncing, and Deleting Private Branches &mdash; Creating,</a></li>
306 <li><a href="custom_ticket.wiki">System &mdash; Customizing The Ticket</a></li>
307 <li><a href="tickets.wiki">System &mdash; The Fossil Ticket</a></li>
308 <li><a href="branching.wiki">Tagging &mdash; Branching, Forking, Merging, and</a></li>
309 <li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil &mdash; A</a></li>
310 <li><a href="../test/release-checklist.wiki">Testing Checklist &mdash; Pre-Release</a></li>
311 <li><a href="th1.md">TH1 Scripting Language &mdash; The</a></li>
312 <li><a href="backoffice.md"><b>The "Backoffice" mechanism of Fossil</b></a></li>
313 <li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li>
314 <li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
315 <li><a href="fileedit-page.md"><b>The fileedit Page</b></a></li>
316 <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
317 <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
318 <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
319 <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
320 <li><a href="pikchr.md"><b>The Pikchr Diagram Language</b></a></li>
321 <li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li>
322 <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
323 <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
324 <li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
325 <li><a href="cap-theorem.md">Theorem &mdash; Fossil and the CAP</a></li>
326 <li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
327 <li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
328 <li><a href="tickets.wiki">Ticket System &mdash; The Fossil</a></li>
329 <li><a href="customgraph.md">Timeline Graph &mdash; Theming: Customizing the</a></li>
330 <li><a href="css-tricks.md">Tips and Tricks &mdash; Fossil CSS</a></li>
331 <li><a href="hints.wiki">Tips And Usage Hints &mdash; Fossil</a></li>
332 <li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
333 <li><a href="gitusers.md">Translation Guide &mdash; Git to Fossil</a></li>
334 <li><a href="css-tricks.md">Tricks &mdash; Fossil CSS Tips and</a></li>
335 <li><a href="unvers.wiki"><b>Unversioned Files</b></a></li>
336 <li><a href="backup.md">Up a Remote Fossil Repository &mdash; Backing</a></li>
337 <li><a href="fiveminutes.wiki"><b>Up and Running in 5 Minutes as a Single User</b></a></li>
338 <li><a href="co-vs-up.md">Update &mdash; Checkout vs</a></li>
339 <li><a href="hints.wiki">Usage Hints &mdash; Fossil Tips And</a></li>
340 <li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
341 <li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
342 <li><a href="caps/">User Capabilities &mdash; Administering</a></li>
343 <li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
344 <li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
345 <li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
346 <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
347 <li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
348 <li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
349 <li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
350 <li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
351 <li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size &mdash; Image Format</a></li>
352 <li><a href="grep.md">vs POSIX grep &mdash; Fossil grep</a></li>
353 <li><a href="co-vs-up.md">vs Update &mdash; Checkout</a></li>
354 <li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
355 <li><a href="customskin.md">Web Pages &mdash; Theming: Customizing The Appearance of</a></li>
356 <li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li>
357 <li><a href="../../../help">Webpages &mdash; Lists of Commands and</a></li>
358 <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
359 <li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li>
360 <li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li>
361 <li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></a></li>
362 <li><a href="ckout-workflows.md">Workflows &mdash; Check-Out</a></li>
363 <li><a href="aboutdownload.wiki">Works &mdash; How The Download Page</a></li>
364 <li><a href="aboutcgi.wiki">Works In Fossil &mdash; How CGI</a></li>
365 <li><a href="whyusefossil.wiki">You Should Use Fossil &mdash; Why</a></li>
366 </ul></div>
367
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -18,349 +18,116 @@
18 <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
19 <li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's
20 book</a>
21 </ul>
22 <a name="pindex"></a>
23 <h2>Other Documents:</h2>
24 <ul>
25 <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li>
26 <li><a href="serverext.wiki">Adding Extensions To A Fossil Server Using CGI Scripts</a></li>
27 <li><a href="adding_code.wiki">Adding New Features To Fossil</a></li>
28 <li><a href="caps/">Administering User Capabilities</a></li>
29 <li><a href="backup.md">Backing Up a Remote Fossil Repository</a></li>
30 <li><a href="whyusefossil.wiki">Benefits Of Version Control</a></li>
31 <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
32 <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
33 <li><a href="cgi.wiki">CGI Script Configuration Options</a></li>
34 <li><a href="serverext.wiki">CGI Server Extensions</a></li>
35 <li><a href="checkin_names.wiki">Check-in And Version Names</a></li>
36 <li><a href="checkin.wiki">Check-in Checklist</a></li>
37 <li><a href="ckout-workflows.md">Check-Out Workflows</a></li>
38 <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li>
39 <li><a href="co-vs-up.md">Checkout vs Update</a></li>
40 <li><a href="childprojects.wiki">Child Projects</a></li>
41 <li><a href="build.wiki">Compiling and Installing Fossil</a></li>
42 <li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li>
43 <li><a href="copyright-release.html">Contributor License Agreement</a></li>
44 <li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li>
45 <li><a href="customskin.md">Custom Skins</a></li>
46 <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li>
47 <li><a href="antibot.wiki">Defense against Spiders and Bots</a></li>
48 <li><a href="delta-manifests.md">Delta Manifests</a></li>
49 <li><a href="contact.md">Developer Contact Information</a></li>
50 <li><a href="caps/admin-v-setup.md">Differences Between Setup and Admin Users</a></li>
51 <li><a href="alerts.md">Email Alerts And Notifications</a></li>
52 <li><a href="embeddeddoc.wiki">Embedded Project Documentation</a></li>
53 <li><a href="env-opts.md">Environment Variables and Global Options</a></li>
54 <li><a href="event.wiki">Events</a></li>
55 <li><a href="globs.md">File Name Glob Patterns</a></li>
56 <li><a href="cap-theorem.md">Fossil and the CAP Theorem</a></li>
57 <li><a href="changes.wiki">Fossil Changelog</a></li>
58 <li><a href="chat.md">Fossil Chat</a></li>
59 <li><a href="concepts.wiki">Fossil Core Concepts</a></li>
60 <li><a href="css-tricks.md">Fossil CSS Tips and Tricks</a></li>
61 <li><a href="delta_encoder_algorithm.wiki">Fossil Delta Encoding Algorithm</a></li>
62 <li><a href="delta_format.wiki">Fossil Delta Format</a></li>
63 <li><a href="hacker-howto.wiki">Fossil Developers Guide</a></li>
64 <li><a href="fileformat.wiki">Fossil File Format</a></li>
65 <li><a href="forum.wiki">Fossil Forums</a></li>
66 <li><a href="grep.md">Fossil grep vs POSIX grep</a></li>
67 <li><a href="quickstart.wiki">Fossil Quick Start Guide</a></li>
68 <li><a href="selfcheck.wiki">Fossil Repository Integrity Self Checks</a></li>
69 <li><a href="selfhost.wiki">Fossil Self Hosting Repositories</a></li>
70 <li><a href="settings.wiki">Fossil Settings</a></li>
71 <li><a href="hints.wiki">Fossil Tips And Usage Hints</a></li>
72 <li><a href="fossil-v-git.wiki">Fossil Versus Git</a></li>
73 <li><a href="fossil_prompt.wiki">Fossilized Bash Prompt</a></li>
74 <li><a href="faq.wiki">Frequently Asked Questions</a></li>
75 <li><a href="gitusers.md">Git to Fossil Translation Guide</a></li>
76 <li><a href="hacker-howto.wiki">Hacker How-To</a></li>
77 <li><a href="adding_code.wiki">Hacking Fossil</a></li>
78 <li><a href="hashpolicy.wiki">Hash Policy: Choosing Between SHA1 and SHA3-256</a></li>
79 <li><a href="hashes.md">Hashes: Fossil Artifact Identification</a></li>
80 <li><a href="index.wiki">Home Page</a></li>
81 <li><a href="aboutcgi.wiki">How CGI Works In Fossil</a></li>
82 <li><a href="aboutdownload.wiki">How The Download Page Works</a></li>
83 <li><a href="server/">How To Configure A Fossil Server</a></li>
84 <li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li>
85 <li><a href="mirrortogithub.md">How To Mirror A Fossil Repository On GitHub</a></li>
86 <li><a href="encryptedrepos.wiki">How To Use Encrypted Repositories</a></li>
87 <li><a href="image-format-vs-repo-size.md">Image Format vs Fossil Repo Size</a></li>
88 <li><a href="inout.wiki">Import And Export To And From Git</a></li>
89 <li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li>
90 <li><a href="interwiki.md">Interwiki Links</a></li>
91 <li><a href="fossil-is-not-relational.md">Introduction to the (Non-relational) Fossil Data Model</a></li>
92 <li><a href="blockchain.md">Is Fossil A Blockchain?</a></li>
93 <li><a href="mirrorlimitations.md">Limitations On Git Mirrors</a></li>
94 <li><a href="../../../help">Lists of Commands and Webpages</a></li>
95 <li><a href="loadmgmt.md">Managing Server Load</a></li>
96 <li><a href="../../../md_rules">Markdown Formatting Rules</a></li>
97 <li><a href="password.wiki">Password Management And Authentication</a></li>
98 <li><a href="stats.wiki">Performance Statistics</a></li>
99 <li><a href="../test/release-checklist.wiki">Pre-Release Testing Checklist</a></li>
100 <li><a href="pop.wiki">Principles Of Operation</a></li>
101 <li><a href="qandc.wiki">Questions And Criticisms</a></li>
102 <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li>
103 <li><a href="rebaseharm.md">Rebase Considered Harmful</a></li>
104 <li><a href="reviews.wiki">Reviews</a></li>
105 <li><a href="chroot.md">Server Chroot Jail</a></li>
106 <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li>
107 <li><a href="../../../sitemap">Site Map</a></li>
108 <li><a href="style.wiki">Source Code Style Guidelines</a></li>
109 <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
110 <li><a href="backoffice.md">The "Backoffice" mechanism of Fossil</a></li>
111 <li><a href="blame.wiki">The Annotate/Blame Algorithm Of Fossil</a></li>
112 <li><a href="defcsp.md">The Default Content Security Policy</a></li>
113 <li><a href="fileedit-page.md">The fileedit Page</a></li>
114 <li><a href="makefile.wiki">The Fossil Build Process</a></li>
115 <li><a href="sync.wiki">The Fossil Sync Protocol</a></li>
116 <li><a href="tickets.wiki">The Fossil Ticket System</a></li>
117 <li><a href="webui.wiki">The Fossil Web Interface</a></li>
118 <li><a href="pikchr.md">The Pikchr Diagram Language</a></li>
119 <li><a href="history.md">The Purpose And History Of Fossil</a></li>
120 <li><a href="th1.md">The TH1 Scripting Language</a></li>
121 <li><a href="customskin.md">Theming: Customizing The Appearance of Web Pages</a></li>
122 <li><a href="customgraph.md">Theming: Customizing the Timeline Graph</a></li>
123 <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li>
124 <li><a href="unvers.wiki">Unversioned Files</a></li>
125 <li><a href="fiveminutes.wiki">Up and Running in 5 Minutes as a Single User</a></li>
126 <li><a href="javascript.md">Use of JavaScript in Fossil</a></li>
127 <li><a href="caps/ref.html">User Capability Reference</a></li>
128 <li><a href="ssl.wiki">Using SSL with Fossil</a></li>
129 <li><a href="webpage-ex.md">Webpage Examples</a></li>
130 <li><a href="whyusefossil.wiki">Why You Should Use Fossil</a></li>
131 <li><a href="../../../wiki_rules">Wiki Formatting Rules</a></li>
132 <li><a href="wikitheory.wiki">Wiki In Fossil</a></li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133 </ul></div>
134
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -1,16 +1,16 @@
1
-<title>Benefits Of A Fossil Server</title>
1
+<title>Benefits of a Fossil Server</title>
22
33
<h2>No Server Required</h2>
44
55
Fossil does not require a central server.
66
Data sharing and synchronization can be entirely peer-to-peer.
77
Fossil uses
88
[https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types]
99
to ensure that (in the limit) all participating peers see the same content.
1010
11
-<h2>But, A Server Can Be Useful</h2>
11
+<h2>But, a Server Can Be Useful</h2>
1212
1313
Fossil does not require a server, but a server can be very useful.
1414
Here are a few reasons to set up a Fossil server for your project:
1515
1616
1. <b>A server works as a complete project website.</b><p>
@@ -27,22 +27,50 @@
2727
syncing their work.</b><p>
2828
It is possible for developers to synchronize peer-to-peer but
2929
that requires the developers coordinate the sync, which in turn
3030
requires that the developers both want to sync at the same moment.
3131
A server alleviates this time dependency by allowing each developer
32
- to sync whenever it is convenient (for example, automatically syncing
33
- after each commit and before each update). Developers all stay
34
- in sync with each other, without having to interrupt each other
32
+ to sync whenever it is convenient. For example, a developer may
33
+ choose to automatically sync
34
+ after each commit and before each update. Developers all stay
35
+ in sync with each other without having to interrupt each other
3536
constantly to set up a peer-to-peer sync.
3637
3738
3. <b>A server provides project leaders with up-to-date status.</b><p>
3839
Project coordinators and BDFLs can click on a link or two at the
39
- central Fossil server for a project, and quickly tell what is
40
- going on. They can do this from anywhere, even from their phones,
41
- without needing to actually sync to the device they are using.
40
+ central Fossil server for a project and quickly tell what is
41
+ going on. They can do this from anywhere — even from their phones
42
+ — without needing to actually sync to the device they are using.
4243
4344
4. <b>A server provides automatic off-site backups.</b><p>
4445
A Fossil server is an automatic remote backup for all the work
45
- going into a project. You can even set up multiple servers, at
46
- multiple sites, with automatic synchronization between them, for
47
- added redundancy. Such a set up means that no work is lost due
46
+ going into a project. ([../backup.md | Within limits].)
47
+ You can even set up multiple servers at
48
+ multiple sites with automatic synchronization between them for
49
+ added redundancy. Such a setup means that no work is lost due
4850
to a single machine failure.
51
+
52
+ 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html
53
+ | SQLite corruption risk mitigation] to a single point.</b><p>
54
+ The concerns in section 1 of that document assume you have direct
55
+ access to the central DB files, which isn't the case when the
56
+ server is remote and secure against tampering.<p>
57
+ Section 2 is about file locking, which concerns disappear when Fossil's
58
+ on the other side of an HTTP boundary and your server is set up
59
+ properly.<p>
60
+ Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations,
61
+ but setting up a server lets you address the risks
62
+ in a single place. Once a given commit is
63
+ sync'd to the server, you can be reasonably sure any client-side
64
+ corruption can be fixed with a fresh clone. Ultimately, this
65
+ is an argument for off-machine backups, which returns us to reason
66
+ #4 above.<p>
67
+ Sections 3.2 and the entirety of section 7 are no concern with
68
+ Fossil at all, since it's primarily written by the creator and
69
+ primary maintainer of SQLite, so you can be certain Fossil doesn't
70
+ actively pursue coding strategies known to risk database corruption.<p>
71
+
72
+ 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b><p>
73
+ The role-based access control (RBAC) system in Fossil only works
74
+ when the remote system is on the other side of an HTTP barrier.
75
+ ([../caps/#webonly | Details].) If you want its benefits, you need
76
+ a Fossil server setup of some kind.
4977
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -1,16 +1,16 @@
1 <title>Benefits Of A Fossil Server</title>
2
3 <h2>No Server Required</h2>
4
5 Fossil does not require a central server.
6 Data sharing and synchronization can be entirely peer-to-peer.
7 Fossil uses
8 [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types]
9 to ensure that (in the limit) all participating peers see the same content.
10
11 <h2>But, A Server Can Be Useful</h2>
12
13 Fossil does not require a server, but a server can be very useful.
14 Here are a few reasons to set up a Fossil server for your project:
15
16 1. <b>A server works as a complete project website.</b><p>
@@ -27,22 +27,50 @@
27 syncing their work.</b><p>
28 It is possible for developers to synchronize peer-to-peer but
29 that requires the developers coordinate the sync, which in turn
30 requires that the developers both want to sync at the same moment.
31 A server alleviates this time dependency by allowing each developer
32 to sync whenever it is convenient (for example, automatically syncing
33 after each commit and before each update). Developers all stay
34 in sync with each other, without having to interrupt each other
 
35 constantly to set up a peer-to-peer sync.
36
37 3. <b>A server provides project leaders with up-to-date status.</b><p>
38 Project coordinators and BDFLs can click on a link or two at the
39 central Fossil server for a project, and quickly tell what is
40 going on. They can do this from anywhere, even from their phones,
41 without needing to actually sync to the device they are using.
42
43 4. <b>A server provides automatic off-site backups.</b><p>
44 A Fossil server is an automatic remote backup for all the work
45 going into a project. You can even set up multiple servers, at
46 multiple sites, with automatic synchronization between them, for
47 added redundancy. Such a set up means that no work is lost due
 
48 to a single machine failure.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -1,16 +1,16 @@
1 <title>Benefits of a Fossil Server</title>
2
3 <h2>No Server Required</h2>
4
5 Fossil does not require a central server.
6 Data sharing and synchronization can be entirely peer-to-peer.
7 Fossil uses
8 [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types]
9 to ensure that (in the limit) all participating peers see the same content.
10
11 <h2>But, a Server Can Be Useful</h2>
12
13 Fossil does not require a server, but a server can be very useful.
14 Here are a few reasons to set up a Fossil server for your project:
15
16 1. <b>A server works as a complete project website.</b><p>
@@ -27,22 +27,50 @@
27 syncing their work.</b><p>
28 It is possible for developers to synchronize peer-to-peer but
29 that requires the developers coordinate the sync, which in turn
30 requires that the developers both want to sync at the same moment.
31 A server alleviates this time dependency by allowing each developer
32 to sync whenever it is convenient. For example, a developer may
33 choose to automatically sync
34 after each commit and before each update. Developers all stay
35 in sync with each other without having to interrupt each other
36 constantly to set up a peer-to-peer sync.
37
38 3. <b>A server provides project leaders with up-to-date status.</b><p>
39 Project coordinators and BDFLs can click on a link or two at the
40 central Fossil server for a project and quickly tell what is
41 going on. They can do this from anywhere — even from their phones
42 — without needing to actually sync to the device they are using.
43
44 4. <b>A server provides automatic off-site backups.</b><p>
45 A Fossil server is an automatic remote backup for all the work
46 going into a project. ([../backup.md | Within limits].)
47 You can even set up multiple servers at
48 multiple sites with automatic synchronization between them for
49 added redundancy. Such a setup means that no work is lost due
50 to a single machine failure.
51
52 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html
53 | SQLite corruption risk mitigation] to a single point.</b><p>
54 The concerns in section 1 of that document assume you have direct
55 access to the central DB files, which isn't the case when the
56 server is remote and secure against tampering.<p>
57 Section 2 is about file locking, which concerns disappear when Fossil's
58 on the other side of an HTTP boundary and your server is set up
59 properly.<p>
60 Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations,
61 but setting up a server lets you address the risks
62 in a single place. Once a given commit is
63 sync'd to the server, you can be reasonably sure any client-side
64 corruption can be fixed with a fresh clone. Ultimately, this
65 is an argument for off-machine backups, which returns us to reason
66 #4 above.<p>
67 Sections 3.2 and the entirety of section 7 are no concern with
68 Fossil at all, since it's primarily written by the creator and
69 primary maintainer of SQLite, so you can be certain Fossil doesn't
70 actively pursue coding strategies known to risk database corruption.<p>
71
72 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b><p>
73 The role-based access control (RBAC) system in Fossil only works
74 when the remote system is on the other side of an HTTP barrier.
75 ([../caps/#webonly | Details].) If you want its benefits, you need
76 a Fossil server setup of some kind.
77
+14
--- www/th1.md
+++ www/th1.md
@@ -128,10 +128,11 @@
128128
* foreach VARIABLE-LIST VALUE-LIST BODY-SCRIPT
129129
* if EXPR SCRIPT (elseif EXPR SCRIPT)* ?else SCRIPT?
130130
* info commands
131131
* info exists VARNAME
132132
* info vars
133
+ * lappend VARIABLE TERM ...
133134
* lindex LIST INDEX
134135
* list ARG ...
135136
* llength LIST
136137
* lsearch LIST STRING
137138
* proc NAME ARG-LIST BODY-SCRIPT
@@ -141,10 +142,11 @@
141142
* string compare STR1 STR2
142143
* string first NEEDLE HAYSTACK ?START-INDEX?
143144
* string index STRING INDEX
144145
* string is CLASS STRING
145146
* string last NEEDLE HAYSTACK ?START-INDEX?
147
+ * string match PATTERN STRING
146148
* string length STRING
147149
* string range STRING FIRST LAST
148150
* string repeat STRING COUNT
149151
* unset VARNAME
150152
* uplevel ?LEVEL? SCRIPT
@@ -168,10 +170,11 @@
168170
features of Fossil. The following is a summary of the extended commands:
169171
170172
* [anoncap](#anoncap)
171173
* [anycap](#anycap)
172174
* [artifact](#artifact)
175
+ * [builtin_request_js](#bireqjs)
173176
* [capexpr](#capexpr)
174177
* [captureTh1](#captureTh1)
175178
* [cgiHeaderLine](#cgiHeaderLine)
176179
* [checkout](#checkout)
177180
* [combobox](#combobox)
@@ -260,10 +263,21 @@
260263
261264
Attempts to locate the specified artifact and return its contents. An
262265
error is generated if the repository is not open or the artifact cannot
263266
be found.
264267
268
+
269
+<a id="bireqjs"></a>TH1 builtin_request_js Command
270
+--------------------------------------------------
271
+
272
+ * builtin_request_js NAME
273
+
274
+NAME must be the name of one of the
275
+[built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$).
276
+This command causes that javascript file to be appended to the delivered
277
+document.
278
+
265279
266280
267281
<a id="capexpr"></a>TH1 capexpr Command
268282
-----------------------------------------------------
269283
270284
--- www/th1.md
+++ www/th1.md
@@ -128,10 +128,11 @@
128 * foreach VARIABLE-LIST VALUE-LIST BODY-SCRIPT
129 * if EXPR SCRIPT (elseif EXPR SCRIPT)* ?else SCRIPT?
130 * info commands
131 * info exists VARNAME
132 * info vars
 
133 * lindex LIST INDEX
134 * list ARG ...
135 * llength LIST
136 * lsearch LIST STRING
137 * proc NAME ARG-LIST BODY-SCRIPT
@@ -141,10 +142,11 @@
141 * string compare STR1 STR2
142 * string first NEEDLE HAYSTACK ?START-INDEX?
143 * string index STRING INDEX
144 * string is CLASS STRING
145 * string last NEEDLE HAYSTACK ?START-INDEX?
 
146 * string length STRING
147 * string range STRING FIRST LAST
148 * string repeat STRING COUNT
149 * unset VARNAME
150 * uplevel ?LEVEL? SCRIPT
@@ -168,10 +170,11 @@
168 features of Fossil. The following is a summary of the extended commands:
169
170 * [anoncap](#anoncap)
171 * [anycap](#anycap)
172 * [artifact](#artifact)
 
173 * [capexpr](#capexpr)
174 * [captureTh1](#captureTh1)
175 * [cgiHeaderLine](#cgiHeaderLine)
176 * [checkout](#checkout)
177 * [combobox](#combobox)
@@ -260,10 +263,21 @@
260
261 Attempts to locate the specified artifact and return its contents. An
262 error is generated if the repository is not open or the artifact cannot
263 be found.
264
 
 
 
 
 
 
 
 
 
 
 
265
266
267 <a id="capexpr"></a>TH1 capexpr Command
268 -----------------------------------------------------
269
270
--- www/th1.md
+++ www/th1.md
@@ -128,10 +128,11 @@
128 * foreach VARIABLE-LIST VALUE-LIST BODY-SCRIPT
129 * if EXPR SCRIPT (elseif EXPR SCRIPT)* ?else SCRIPT?
130 * info commands
131 * info exists VARNAME
132 * info vars
133 * lappend VARIABLE TERM ...
134 * lindex LIST INDEX
135 * list ARG ...
136 * llength LIST
137 * lsearch LIST STRING
138 * proc NAME ARG-LIST BODY-SCRIPT
@@ -141,10 +142,11 @@
142 * string compare STR1 STR2
143 * string first NEEDLE HAYSTACK ?START-INDEX?
144 * string index STRING INDEX
145 * string is CLASS STRING
146 * string last NEEDLE HAYSTACK ?START-INDEX?
147 * string match PATTERN STRING
148 * string length STRING
149 * string range STRING FIRST LAST
150 * string repeat STRING COUNT
151 * unset VARNAME
152 * uplevel ?LEVEL? SCRIPT
@@ -168,10 +170,11 @@
170 features of Fossil. The following is a summary of the extended commands:
171
172 * [anoncap](#anoncap)
173 * [anycap](#anycap)
174 * [artifact](#artifact)
175 * [builtin_request_js](#bireqjs)
176 * [capexpr](#capexpr)
177 * [captureTh1](#captureTh1)
178 * [cgiHeaderLine](#cgiHeaderLine)
179 * [checkout](#checkout)
180 * [combobox](#combobox)
@@ -260,10 +263,21 @@
263
264 Attempts to locate the specified artifact and return its contents. An
265 error is generated if the repository is not open or the artifact cannot
266 be found.
267
268
269 <a id="bireqjs"></a>TH1 builtin_request_js Command
270 --------------------------------------------------
271
272 * builtin_request_js NAME
273
274 NAME must be the name of one of the
275 [built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$).
276 This command causes that javascript file to be appended to the delivered
277 document.
278
279
280
281 <a id="capexpr"></a>TH1 capexpr Command
282 -----------------------------------------------------
283
284

Keyboard Shortcuts

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