Fossil SCM
Merge from trunk
Commit
1a403a7a0c142074a3d63884bbd0d12d9efcb0f74ce383616850449b3043d18f
Parent
7e755346a56adb1…
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
~
auto.def
~
skins/README.md
~
skins/darkmode/css.txt
~
skins/khaki/css.txt
~
skins/xekri/css.txt
~
src/add.c
~
src/ajax.c
~
src/alerts.c
~
src/backoffice.c
~
src/blob.c
~
src/branch.c
~
src/browse.c
~
src/builtin.c
~
src/bundle.c
~
src/cgi.c
~
src/checkin.c
~
src/checkout.c
~
src/configure.c
~
src/content.c
~
src/cookies.c
~
src/db.c
~
src/default.css
~
src/descendants.c
~
src/dispatch.c
~
src/doc.c
~
src/export.c
~
src/fileedit.c
~
src/finfo.c
~
src/fossil.info-diff.js
~
src/hook.c
~
src/info.c
~
src/json_branch.c
~
src/login.c
~
src/lookslike.c
~
src/main.c
~
src/main.mk
~
src/main.mk
~
src/manifest.c
~
src/merge3.c
~
src/name.c
~
src/pikchrshow.c
~
src/purge.c
~
src/rebuild.c
~
src/rss.c
~
src/setup.c
~
src/sha1.c
~
src/sha3.c
~
src/shell.c
~
src/skins.c
~
src/sqlcompattest.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/tag.c
~
src/tar.c
~
src/timeline.c
~
src/tkt.c
~
src/undo.c
~
src/unversioned.c
~
src/user.c
~
src/wiki.c
~
src/wiki.c
~
src/winhttp.c
~
test/release-checklist.wiki
~
win/Makefile.mingw
~
win/Makefile.msc
~
www/backup.md
~
www/cgi.wiki
~
www/changes.wiki
~
www/chat.md
~
www/customskin.md
~
www/defcsp.md
~
www/delta-manifests.md
~
www/gsoc-ideas.md
~
www/index.wiki
~
www/makefile.wiki
~
www/mkindex.tcl
~
www/permutedindex.html
~
www/server/whyuseaserver.wiki
~
www/th1.md
M
auto.def
+19
| --- auto.def | ||
| +++ auto.def | ||
| @@ -1,7 +1,9 @@ | ||
| 1 | 1 | # System autoconfiguration. Try: ./configure --help |
| 2 | 2 | |
| 3 | +# This must be above "options" below because it implicitly brings in the | |
| 4 | +# default Autosetup options, things like --prefix. | |
| 3 | 5 | use cc cc-lib |
| 4 | 6 | |
| 5 | 7 | options { |
| 6 | 8 | with-openssl:path|auto|tree|none |
| 7 | 9 | => {Look for OpenSSL in the given path, automatically, in the source tree, or none} |
| @@ -17,17 +19,31 @@ | ||
| 17 | 19 | with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism} |
| 18 | 20 | with-tcl-private-stubs=0 |
| 19 | 21 | => {Enable Tcl integration via private stubs mechanism} |
| 20 | 22 | with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"} |
| 21 | 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} | |
| 22 | 26 | internal-sqlite=1 => {Don't use the internal SQLite, use the system one} |
| 23 | 27 | static=0 => {Link a static executable} |
| 24 | 28 | fusefs=1 => {Disable the Fuse Filesystem} |
| 25 | 29 | fossil-debug=0 => {Build with fossil debugging enabled} |
| 26 | 30 | no-opt=0 => {Build without optimization} |
| 27 | 31 | json=0 => {Build with fossil JSON API enabled} |
| 28 | 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 | +} | |
| 29 | 45 | |
| 30 | 46 | # sqlite wants these types if possible |
| 31 | 47 | cc-with {-includes {stdint.h inttypes.h}} { |
| 32 | 48 | cc-check-types uint32_t uint16_t int16_t uint8_t |
| 33 | 49 | } |
| @@ -154,17 +170,20 @@ | ||
| 154 | 170 | find_system_sqlite |
| 155 | 171 | |
| 156 | 172 | proc test_system_sqlite {} { |
| 157 | 173 | # Check compatibility of the system SQLite library by running the sqlcompttest.c |
| 158 | 174 | # program in the source tree |
| 175 | + # passes MINIMUM_SQLITE_VERSION set at the top of this file to sqlcompttest.c | |
| 159 | 176 | # |
| 160 | 177 | set cmdline {} |
| 161 | 178 | lappend cmdline {*}[get-define CCACHE] |
| 162 | 179 | lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] |
| 163 | 180 | lappend cmdline $::autosetup(dir)/../src/sqlcompattest.c -o conftest__ |
| 164 | 181 | lappend cmdline {*}[get-define LDFLAGS] |
| 165 | 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] | |
| 166 | 185 | set ok 1 |
| 167 | 186 | set err [catch {exec-with-stderr {*}$cmdline} result errinfo] |
| 168 | 187 | if {$err} { |
| 169 | 188 | configlog "Failed: [join $cmdline]" |
| 170 | 189 | if {[string length $result]>0} {configlog $result} |
| 171 | 190 |
| --- 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 @@ | ||
| 30 | 30 | 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source |
| 31 | 31 | file so that it describes and references the "newskin" skin. |
| 32 | 32 | |
| 33 | 33 | 5. Type "make" to rebuild. |
| 34 | 34 | |
| 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. | |
| 36 | 37 | |
| 37 | 38 | Development Hints |
| 38 | 39 | ----------------- |
| 39 | 40 | |
| 40 | 41 | One way to develop a new skin is to copy the baseline files (css.txt, |
| 41 | 42 |
| --- 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 @@ | ||
| 134 | 134 | color: #333; |
| 135 | 135 | border-color: #888; |
| 136 | 136 | outline: 0 |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | -div.mainmenu a.active { | |
| 140 | - opacity: 0.8; | |
| 141 | -} | |
| 142 | - | |
| 143 | 139 | /* All page content from the bottom of the menu or submenu down to |
| 144 | 140 | ** the footer */ |
| 145 | 141 | div.content { |
| 146 | 142 | padding: 0ex 1ex 1ex 1ex; |
| 147 | 143 | } |
| 148 | 144 |
| --- 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 |
+7
| --- skins/khaki/css.txt | ||
| +++ skins/khaki/css.txt | ||
| @@ -163,5 +163,12 @@ | ||
| 163 | 163 | table.label-value th { |
| 164 | 164 | vertical-align: top; |
| 165 | 165 | text-align: right; |
| 166 | 166 | padding: 0.2ex 2ex; |
| 167 | 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 | +} | |
| 168 | 175 |
| --- 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 |
+16
-2
| --- skins/xekri/css.txt | ||
| +++ skins/xekri/css.txt | ||
| @@ -28,11 +28,11 @@ | ||
| 28 | 28 | -ms-text-size-adjust: none; |
| 29 | 29 | -webkit-text-size-adjust: none; |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | a { |
| 33 | - color: #07e; | |
| 33 | + color: #40a0ff; | |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | a:hover { |
| 37 | 37 | font-weight: bold; |
| 38 | 38 | } |
| @@ -115,11 +115,11 @@ | ||
| 115 | 115 | vertical-align: top; |
| 116 | 116 | white-space: nowrap; |
| 117 | 117 | } |
| 118 | 118 | |
| 119 | 119 | div.title { |
| 120 | - color: #07e; | |
| 120 | + color: #3297f9; | |
| 121 | 121 | font-family: Verdana, sans-serif; |
| 122 | 122 | font-weight: bold; |
| 123 | 123 | font-size: 2.5rem; |
| 124 | 124 | padding: 0.5rem; |
| 125 | 125 | text-align: center; |
| @@ -1124,5 +1124,19 @@ | ||
| 1124 | 1124 | .fossil-PopupWidget, |
| 1125 | 1125 | .fossil-tooltip.help-buttonlet-content { |
| 1126 | 1126 | background-color: #111; |
| 1127 | 1127 | border: 1px solid rgba(255,255,255,0.5); |
| 1128 | 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 | +} | |
| 1129 | 1143 |
| --- 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 @@ | ||
| 363 | 363 | ** be treated case sensitive or not. If the option is not given, the default |
| 364 | 364 | ** depends on the global setting, or the operating system default, if not set. |
| 365 | 365 | ** |
| 366 | 366 | ** Options: |
| 367 | 367 | ** |
| 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 (".") | |
| 370 | 370 | ** -f|--force Add files without prompting |
| 371 | 371 | ** --ignore CSG Ignore unmanaged files matching patterns from |
| 372 | 372 | ** the Comma Separated Glob (CSG) pattern list |
| 373 | 373 | ** --clean CSG Also ignore files matching patterns from |
| 374 | 374 | ** the Comma Separated Glob (CSG) list |
| @@ -375,14 +375,17 @@ | ||
| 375 | 375 | ** --reset Reset the ADDED state of a checkout, such |
| 376 | 376 | ** that all newly-added (but not yet committed) |
| 377 | 377 | ** files are no longer added. No flags other |
| 378 | 378 | ** than --verbose and --dry-run may be used |
| 379 | 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. | |
| 380 | 383 | ** |
| 381 | 384 | ** 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 | |
| 384 | 387 | ** |
| 385 | 388 | ** See also: [[addremove]], [[rm]] |
| 386 | 389 | */ |
| 387 | 390 | void add_cmd(void){ |
| 388 | 391 | int i; /* Loop counter */ |
| @@ -391,10 +394,11 @@ | ||
| 391 | 394 | const char *zCleanFlag; /* The --clean option or clean-glob setting */ |
| 392 | 395 | const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */ |
| 393 | 396 | Glob *pIgnore, *pClean; /* Ignore everything matching the glob patterns */ |
| 394 | 397 | unsigned scanFlags = 0; /* Flags passed to vfile_scan() */ |
| 395 | 398 | int forceFlag; |
| 399 | + int allowReservedFlag = 0; /* --allow-reserved flag */ | |
| 396 | 400 | |
| 397 | 401 | if(0!=find_option("reset",0,0)){ |
| 398 | 402 | int const verboseFlag = find_option("verbose","v",0)!=0; |
| 399 | 403 | int const dryRunFlag = find_option("dry-run","n",0)!=0; |
| 400 | 404 | db_must_be_within_tree(); |
| @@ -405,10 +409,11 @@ | ||
| 405 | 409 | |
| 406 | 410 | zCleanFlag = find_option("clean",0,1); |
| 407 | 411 | zIgnoreFlag = find_option("ignore",0,1); |
| 408 | 412 | forceFlag = find_option("force","f",0)!=0; |
| 409 | 413 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 414 | + allowReservedFlag = find_option("allow-reserved",0,0)!=0; | |
| 410 | 415 | |
| 411 | 416 | /* We should be done with options.. */ |
| 412 | 417 | verify_all_options(); |
| 413 | 418 | |
| 414 | 419 | db_must_be_within_tree(); |
| @@ -429,17 +434,27 @@ | ||
| 429 | 434 | |
| 430 | 435 | /* Load the names of all files that are to be added into sfile temp table */ |
| 431 | 436 | for(i=2; i<g.argc; i++){ |
| 432 | 437 | char *zName; |
| 433 | 438 | int isDir; |
| 434 | - Blob fullName; | |
| 439 | + Blob fullName = empty_blob; | |
| 435 | 440 | |
| 436 | 441 | /* file_tree_name() throws a fatal error if g.argv[i] is outside of the |
| 437 | 442 | ** checkout. */ |
| 438 | 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 | + } | |
| 439 | 455 | blob_reset(&fullName); |
| 440 | - | |
| 441 | 456 | file_canonical_name(g.argv[i], &fullName, 0); |
| 442 | 457 | zName = blob_str(&fullName); |
| 443 | 458 | isDir = file_isdir(zName, RepoFILE); |
| 444 | 459 | if( isDir==1 ){ |
| 445 | 460 | vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); |
| @@ -989,13 +1004,13 @@ | ||
| 989 | 1004 | ** as well. This does NOT apply to the 'rename' command. |
| 990 | 1005 | ** |
| 991 | 1006 | ** Options: |
| 992 | 1007 | ** --soft Skip moving files within the checkout. |
| 993 | 1008 | ** 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 | |
| 997 | 1012 | ** |
| 998 | 1013 | ** See also: [[changes]], [[status]] |
| 999 | 1014 | */ |
| 1000 | 1015 | void mv_cmd(void){ |
| 1001 | 1016 | int i; |
| 1002 | 1017 |
| --- 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 @@ | ||
| 66 | 66 | } |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | 69 | /* |
| 70 | 70 | ** 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 | |
| 72 | 72 | ** AJAX_RENDER_PLAIN_TEXT. |
| 73 | 73 | */ |
| 74 | 74 | int ajax_render_mode_for_mimetype(const char * zMimetype){ |
| 75 | 75 | int rc = AJAX_RENDER_PLAIN_TEXT; |
| 76 | 76 | if( zMimetype ){ |
| 77 | 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 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 @@ | ||
| 1020 | 1020 | ** configuration. Options: |
| 1021 | 1021 | ** |
| 1022 | 1022 | ** --body FILENAME |
| 1023 | 1023 | ** --smtp-trace |
| 1024 | 1024 | ** --stdout |
| 1025 | -** --subject|-S SUBJECT | |
| 1025 | +** -S|--subject SUBJECT | |
| 1026 | 1026 | ** |
| 1027 | 1027 | ** unsubscribe EMAIL Remove a single subscriber with the given EMAIL. |
| 1028 | 1028 | */ |
| 1029 | 1029 | void alert_cmd(void){ |
| 1030 | 1030 | const char *zCmd; |
| 1031 | 1031 |
| --- 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 |
+1
-1
| --- src/backoffice.c | ||
| +++ src/backoffice.c | ||
| @@ -695,11 +695,11 @@ | ||
| 695 | 695 | ** backoffice has run recently, return immediately. |
| 696 | 696 | ** |
| 697 | 697 | ** --nolease Always run backoffice, even if there is a lease |
| 698 | 698 | ** conflict. This option implies --nodelay. This |
| 699 | 699 | ** option is added to secondary backoffice commands |
| 700 | -** that are invoked by the --poll option. | |
| 700 | +** that are invoked by the --poll option. | |
| 701 | 701 | */ |
| 702 | 702 | void backoffice_command(void){ |
| 703 | 703 | int nPoll; |
| 704 | 704 | int nMin; |
| 705 | 705 | const char *zPoll; |
| 706 | 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 |
| --- 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 @@ | ||
| 63 | 63 | /* |
| 64 | 64 | ** Seek whence parameter values |
| 65 | 65 | */ |
| 66 | 66 | #define BLOB_SEEK_SET 1 |
| 67 | 67 | #define BLOB_SEEK_CUR 2 |
| 68 | -#define BLOB_SEEK_END 3 | |
| 69 | 68 | |
| 70 | 69 | #endif /* INTERFACE */ |
| 71 | 70 | |
| 72 | 71 | /* |
| 73 | 72 | ** Make sure a blob is initialized |
| @@ -579,12 +578,10 @@ | ||
| 579 | 578 | int blob_seek(Blob *p, int offset, int whence){ |
| 580 | 579 | if( whence==BLOB_SEEK_SET ){ |
| 581 | 580 | p->iCursor = offset; |
| 582 | 581 | }else if( whence==BLOB_SEEK_CUR ){ |
| 583 | 582 | p->iCursor += offset; |
| 584 | - }else if( whence==BLOB_SEEK_END ){ | |
| 585 | - p->iCursor = p->nUsed + offset - 1; | |
| 586 | 583 | } |
| 587 | 584 | if( p->iCursor>p->nUsed ){ |
| 588 | 585 | p->iCursor = p->nUsed; |
| 589 | 586 | } |
| 590 | 587 | return p->iCursor; |
| 591 | 588 |
| --- 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 @@ | ||
| 384 | 384 | ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward). |
| 385 | 385 | ** Either no timezone suffix or "Z" means UTC. |
| 386 | 386 | ** |
| 387 | 387 | ** Options valid for all subcommands: |
| 388 | 388 | ** |
| 389 | -** -R|--repository FILE Run commands on repository FILE | |
| 389 | +** -R|--repository REPO Run commands on repository REPO | |
| 390 | 390 | */ |
| 391 | 391 | void branch_cmd(void){ |
| 392 | 392 | int n; |
| 393 | 393 | const char *zCmd = "list"; |
| 394 | 394 | db_find_and_open_repository(0, 0); |
| @@ -683,11 +683,10 @@ | ||
| 683 | 683 | style_set_current_feature("branch"); |
| 684 | 684 | style_header("Branches"); |
| 685 | 685 | style_submenu_element("List", "brlist"); |
| 686 | 686 | login_anonymous_available(); |
| 687 | 687 | timeline_ss_submenu(); |
| 688 | - cookie_render(); | |
| 689 | 688 | @ <h2>The initial check-in for each branch:</h2> |
| 690 | 689 | blob_append(&sql, timeline_query_for_www(), -1); |
| 691 | 690 | blob_append_sql(&sql, |
| 692 | 691 | "AND blob.rid IN (SELECT rid FROM tagxref" |
| 693 | 692 | " WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH); |
| 694 | 693 |
| --- 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 @@ | ||
| 127 | 127 | ** query parameter is present. If ci= is missing, the union of files |
| 128 | 128 | ** across all check-ins is shown. |
| 129 | 129 | ** |
| 130 | 130 | ** Query parameters: |
| 131 | 131 | ** |
| 132 | +** ci=LABEL Show only files in this check-in. Optional. | |
| 132 | 133 | ** 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 | |
| 134 | 135 | ** type=TYPE TYPE=flat: use this display |
| 135 | 136 | ** TYPE=tree: use the /tree display instead |
| 136 | 137 | ** noreadme Do not attempt to display the README file. |
| 137 | 138 | */ |
| 138 | 139 | void page_dir(void){ |
| @@ -150,10 +151,12 @@ | ||
| 150 | 151 | int linkTip = 1; |
| 151 | 152 | HQuery sURI; |
| 152 | 153 | int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ |
| 153 | 154 | int isBranchCI = 0; /* True if ci= refers to a branch name */ |
| 154 | 155 | char *zHeader = 0; |
| 156 | + const char *zRegexp; /* The re= query parameter */ | |
| 157 | + char *zMatch; /* Extra title text describing the match */ | |
| 155 | 158 | |
| 156 | 159 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 157 | 160 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 158 | 161 | login_check_credentials(); |
| 159 | 162 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -192,10 +195,17 @@ | ||
| 192 | 195 | if( zCI ){ |
| 193 | 196 | zHeader = mprintf("Files in %s/ of %s", zD, zCI); |
| 194 | 197 | }else{ |
| 195 | 198 | zHeader = mprintf("All File in %s/", zD); |
| 196 | 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 = ""; | |
| 197 | 207 | } |
| 198 | 208 | style_header("%s", zHeader); |
| 199 | 209 | fossil_free(zHeader); |
| 200 | 210 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 201 | 211 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| @@ -217,16 +227,19 @@ | ||
| 217 | 227 | @ <h2>Files in the top-level directory \ |
| 218 | 228 | zPrefix = ""; |
| 219 | 229 | } |
| 220 | 230 | if( zCI ){ |
| 221 | 231 | 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> | |
| 223 | 234 | }else if( isBranchCI ){ |
| 224 | 235 | @ 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> | |
| 226 | 238 | }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> | |
| 228 | 241 | } |
| 229 | 242 | zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix); |
| 230 | 243 | if( nD==0 ){ |
| 231 | 244 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 232 | 245 | } |
| @@ -302,10 +315,18 @@ | ||
| 302 | 315 | db_multi_exec( |
| 303 | 316 | "INSERT OR IGNORE INTO localfiles" |
| 304 | 317 | " SELECT pathelement(name,0), NULL FROM filename" |
| 305 | 318 | ); |
| 306 | 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 | + } | |
| 307 | 328 | |
| 308 | 329 | /* Generate a multi-column table listing the contents of zD[] |
| 309 | 330 | ** directory. |
| 310 | 331 | */ |
| 311 | 332 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 312 | 333 |
| --- 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 |
+1
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -704,10 +704,11 @@ | ||
| 704 | 704 | /* This list ordering isn't strictly important. */ |
| 705 | 705 | {"confirmer", 0, 0}, |
| 706 | 706 | {"copybutton", 0, "dom\0"}, |
| 707 | 707 | {"dom", 0, 0}, |
| 708 | 708 | {"fetch", 0, 0}, |
| 709 | + {"info-diff", 0, "dom\0"}, | |
| 709 | 710 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 710 | 711 | {"pikchr", 0, "dom\0"}, |
| 711 | 712 | {"popupwidget", 0, "dom\0"}, |
| 712 | 713 | {"storage", 0, 0}, |
| 713 | 714 | {"tabs", 0, "dom\0"} |
| 714 | 715 |
| --- 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 @@ | ||
| 740 | 740 | ** subset of the check-ins in the repository (usually a single branch) |
| 741 | 741 | ** described by the --branch, --from, --to, and/or --checkin options, |
| 742 | 742 | ** at least one of which is required. If BUNDLE already exists, the |
| 743 | 743 | ** specified content is added to the bundle. |
| 744 | 744 | ** |
| 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 | |
| 747 | 747 | ** --checkin TAG Package the single check-in TAG |
| 748 | 748 | ** --standalone Do no use delta-encoding against |
| 749 | 749 | ** artifacts not in the bundle |
| 750 | 750 | ** |
| 751 | 751 | ** > fossil bundle extend BUNDLE |
| 752 | 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 |
| --- 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 @@ | ||
| 1159 | 1159 | #endif |
| 1160 | 1160 | z = (char*)P("HTTP_COOKIE"); |
| 1161 | 1161 | if( z ){ |
| 1162 | 1162 | z = fossil_strdup(z); |
| 1163 | 1163 | add_param_list(z, ';'); |
| 1164 | + z = (char*)cookie_value("skin",0); | |
| 1165 | + if(z){ | |
| 1166 | + skin_use_alternative(z, 2); | |
| 1167 | + } | |
| 1164 | 1168 | } |
| 1165 | 1169 | |
| 1166 | 1170 | z = (char*)P("QUERY_STRING"); |
| 1167 | 1171 | if( z ){ |
| 1168 | 1172 | z = fossil_strdup(z); |
| 1169 | 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 | + } | |
| 1170 | 1182 | } |
| 1171 | 1183 | |
| 1172 | 1184 | z = (char*)P("REMOTE_ADDR"); |
| 1173 | 1185 | if( z ){ |
| 1174 | 1186 | g.zIpAddr = fossil_strdup(z); |
| 1175 | 1187 |
| --- 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 @@ | ||
| 677 | 677 | ** Options: |
| 678 | 678 | ** --age Show when each file was committed. |
| 679 | 679 | ** -v|--verbose Provide extra information about each file. |
| 680 | 680 | ** -t Sort output in time order. |
| 681 | 681 | ** -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. | |
| 683 | 683 | ** --hash With -v, verify file status using hashing |
| 684 | 684 | ** rather than relying on file sizes and mtimes. |
| 685 | 685 | ** |
| 686 | 686 | ** See also: [[changes]], [[extras]], [[status]] |
| 687 | 687 | */ |
| @@ -831,17 +831,17 @@ | ||
| 831 | 831 | ** |
| 832 | 832 | ** Pathnames are displayed according to the "relative-paths" setting, |
| 833 | 833 | ** unless overridden by the --abs-paths or --rel-paths options. |
| 834 | 834 | ** |
| 835 | 835 | ** Options: |
| 836 | -** --abs-paths Display absolute pathnames. | |
| 836 | +** --abs-paths Display absolute pathnames | |
| 837 | 837 | ** --case-sensitive BOOL Override case-sensitive setting |
| 838 | 838 | ** --dotfiles Include files beginning with a dot (".") |
| 839 | 839 | ** --header Identify the repository if there are extras |
| 840 | 840 | ** --ignore CSG Ignore files matching patterns from the argument |
| 841 | 841 | ** --rel-paths Display pathnames relative to the current working |
| 842 | -** directory. | |
| 842 | +** directory | |
| 843 | 843 | ** |
| 844 | 844 | ** See also: [[changes]], [[clean]], [[status]] |
| 845 | 845 | */ |
| 846 | 846 | void extras_cmd(void){ |
| 847 | 847 | Blob report = BLOB_INITIALIZER; |
| @@ -928,19 +928,19 @@ | ||
| 928 | 928 | ** be removed. Using this option will automatically |
| 929 | 929 | ** enable the --emptydirs option as well. |
| 930 | 930 | ** --disable-undo WARNING: This option disables use of the undo |
| 931 | 931 | ** mechanism for this clean operation and should be |
| 932 | 932 | ** used with extreme caution. |
| 933 | -** --dotfiles Include files beginning with a dot ("."). | |
| 933 | +** --dotfiles Include files beginning with a dot (".") | |
| 934 | 934 | ** --emptydirs Remove any empty directories that are not |
| 935 | 935 | ** explicitly exempted via the empty-dirs setting |
| 936 | 936 | ** or another applicable setting or command line |
| 937 | 937 | ** argument. Matching files, if any, are removed |
| 938 | 938 | ** prior to checking for any empty directories; |
| 939 | 939 | ** therefore, directories that contain only files |
| 940 | 940 | ** that were removed will be removed as well. |
| 941 | -** -f|--force Remove files without prompting. | |
| 941 | +** -f|--force Remove files without prompting | |
| 942 | 942 | ** -i|--prompt Prompt before removing each file. This option |
| 943 | 943 | ** implies the --disable-undo option. |
| 944 | 944 | ** -x|--verily WARNING: Removes everything that is not a managed |
| 945 | 945 | ** file or the repository itself. This option |
| 946 | 946 | ** implies the --force, --emptydirs, --dotfiles, and |
| @@ -951,19 +951,19 @@ | ||
| 951 | 951 | ** --clean CSG WARNING: Never prompt to delete any files matching |
| 952 | 952 | ** this comma separated list of glob patterns. Also, |
| 953 | 953 | ** deletions of any files matching this pattern list |
| 954 | 954 | ** cannot be undone. |
| 955 | 955 | ** --ignore CSG Ignore files matching patterns from the |
| 956 | -** comma separated list of glob patterns. | |
| 956 | +** comma separated list of glob patterns | |
| 957 | 957 | ** --keep <CSG> Keep files matching this comma separated |
| 958 | -** list of glob patterns. | |
| 958 | +** list of glob patterns | |
| 959 | 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. | |
| 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 | 965 | ** |
| 966 | 966 | ** See also: [[addremove]], [[extras]], [[status]] |
| 967 | 967 | */ |
| 968 | 968 | void clean_cmd(void){ |
| 969 | 969 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| @@ -1739,11 +1739,11 @@ | ||
| 1739 | 1739 | char *zMergeUuid; |
| 1740 | 1740 | int mid = db_column_int(&q, 0); |
| 1741 | 1741 | if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ){ |
| 1742 | 1742 | continue; |
| 1743 | 1743 | } |
| 1744 | - zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); | |
| 1744 | + zMergeUuid = rid_to_uuid(mid); | |
| 1745 | 1745 | if( zMergeUuid ){ |
| 1746 | 1746 | blob_appendf(pOut, " %s", zMergeUuid); |
| 1747 | 1747 | if( p->verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate); |
| 1748 | 1748 | free(zMergeUuid); |
| 1749 | 1749 | } |
| 1750 | 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 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 @@ | ||
| 282 | 282 | ** --force Ignore edited files in the current checkout |
| 283 | 283 | ** --keep Only update the manifest and manifest.uuid files |
| 284 | 284 | ** --force-missing Force checkout even if content is missing |
| 285 | 285 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 286 | 286 | ** times (the timestamp of the last checkin which modified |
| 287 | -** them). | |
| 287 | +** them) | |
| 288 | 288 | ** |
| 289 | 289 | ** See also: [[update]] |
| 290 | 290 | */ |
| 291 | 291 | void checkout_cmd(void){ |
| 292 | 292 | int forceFlag; /* Force checkout even if edits exist */ |
| 293 | 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 |
| --- 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 @@ | ||
| 769 | 769 | ** |
| 770 | 770 | ** Synchronize configuration changes in the local repository with |
| 771 | 771 | ** the remote repository at URL. |
| 772 | 772 | ** |
| 773 | 773 | ** Options: |
| 774 | -** -R|--repository FILE Extract info from repository FILE | |
| 774 | +** -R|--repository REPO Extract info from repository REPO | |
| 775 | 775 | ** |
| 776 | 776 | ** See also: [[settings]], [[unset]] |
| 777 | 777 | */ |
| 778 | 778 | void configuration_cmd(void){ |
| 779 | 779 | int n; |
| 780 | 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 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 @@ | ||
| 324 | 324 | ** Extract an artifact by its artifact hash and write the results on |
| 325 | 325 | ** standard output, or if the optional 4th argument is given, in |
| 326 | 326 | ** the named output file. |
| 327 | 327 | ** |
| 328 | 328 | ** Options: |
| 329 | -** -R|--repository FILE Extract artifacts from repository FILE | |
| 329 | +** -R|--repository REPO Extract artifacts from repository REPO | |
| 330 | 330 | ** |
| 331 | 331 | ** See also: [[finfo]] |
| 332 | 332 | */ |
| 333 | 333 | void artifact_cmd(void){ |
| 334 | 334 | int rid; |
| 335 | 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 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 @@ | ||
| 173 | 173 | const char *zDflt /* Default value for the parameter */ |
| 174 | 174 | ){ |
| 175 | 175 | cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE); |
| 176 | 176 | } |
| 177 | 177 | |
| 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. | |
| 180 | 181 | */ |
| 181 | 182 | void cookie_render(void){ |
| 182 | - if( cookies.bChanged && P("udc")!=0 ){ | |
| 183 | + if( cookies.bChanged ){ | |
| 183 | 184 | Blob new; |
| 184 | 185 | int i; |
| 185 | 186 | blob_init(&new, 0, 0); |
| 186 | 187 | for(i=0;i<cookies.nParam;i++){ |
| 187 | 188 | if( i>0 ) blob_append(&new, ",", 1); |
| 188 | 189 |
| --- 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 |
M
src/db.c
+1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -4218,10 +4218,11 @@ | ||
| 4218 | 4218 | ** the following default Content Security Policy is used: |
| 4219 | 4219 | ** |
| 4220 | 4220 | ** default-src 'self' data:; |
| 4221 | 4221 | ** script-src 'self' 'nonce-$nonce'; |
| 4222 | 4222 | ** style-src 'self' 'unsafe-inline'; |
| 4223 | +** img-src * data:; | |
| 4223 | 4224 | ** |
| 4224 | 4225 | ** The default CSP is recommended. The main reason to change |
| 4225 | 4226 | ** this setting would be to add CDNs from which it is safe to |
| 4226 | 4227 | ** load additional content. |
| 4227 | 4228 | */ |
| 4228 | 4229 |
| --- 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 |
+3
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1763,10 +1763,13 @@ | ||
| 1763 | 1763 | body.chat #chat-drop-details img { |
| 1764 | 1764 | max-width: 45%; |
| 1765 | 1765 | max-height: 45%; |
| 1766 | 1766 | } |
| 1767 | 1767 | |
| 1768 | +input[type="checkbox"].diff-toggle { | |
| 1769 | + float: right; | |
| 1770 | +} | |
| 1768 | 1771 | /* Objects in the "desktoponly" class are invisible on mobile */ |
| 1769 | 1772 | @media screen and (max-width: 600px) { |
| 1770 | 1773 | .desktoponly { |
| 1771 | 1774 | display: none; |
| 1772 | 1775 | } |
| 1773 | 1776 |
| --- 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 |
+23
-9
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -343,11 +343,11 @@ | ||
| 343 | 343 | ** |
| 344 | 344 | ** Find all leaf descendants of the check-in specified or if the argument |
| 345 | 345 | ** is omitted, of the check-in currently checked out. |
| 346 | 346 | ** |
| 347 | 347 | ** Options: |
| 348 | -** -R|--repository FILE Extract info from repository FILE | |
| 348 | +** -R|--repository REPO Extract info from repository REPO | |
| 349 | 349 | ** -W|--width N Width of lines (default is to auto-detect). |
| 350 | 350 | ** Must be greater than 20 or else 0 for no |
| 351 | 351 | ** limit, resulting in a one line per entry. |
| 352 | 352 | ** |
| 353 | 353 | ** See also: [[finfo]], [[info]], [[leaves]] |
| @@ -399,15 +399,15 @@ | ||
| 399 | 399 | ** |
| 400 | 400 | ** The --recompute flag causes the content of the "leaf" table in the |
| 401 | 401 | ** repository database to be recomputed. |
| 402 | 402 | ** |
| 403 | 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 | |
| 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 | 409 | ** -W|--width N Width of lines (default is to auto-detect). Must be |
| 410 | 410 | ** more than 39 or else 0 no limit, resulting in a single |
| 411 | 411 | ** line per entry. |
| 412 | 412 | ** |
| 413 | 413 | ** See also: [[descendants]], [[finfo]], [[info]], [[branch]] |
| @@ -422,10 +422,11 @@ | ||
| 422 | 422 | int multipleFlag = find_option("multiple","m",0)!=0; |
| 423 | 423 | const char *zWidth = find_option("width","W",1); |
| 424 | 424 | char *zLastBr = 0; |
| 425 | 425 | int n, width; |
| 426 | 426 | char zLineNo[10]; |
| 427 | + char * const zMainBranch = db_get("main-branch","trunk"); | |
| 427 | 428 | |
| 428 | 429 | if( multipleFlag ) byBranch = 1; |
| 429 | 430 | if( zWidth ){ |
| 430 | 431 | width = atoi(zWidth); |
| 431 | 432 | if( (width!=0) && (width<=39) ){ |
| @@ -491,11 +492,12 @@ | ||
| 491 | 492 | while( db_step(&q)==SQLITE_ROW ){ |
| 492 | 493 | const char *zId = db_column_text(&q, 1); |
| 493 | 494 | const char *zDate = db_column_text(&q, 2); |
| 494 | 495 | const char *zCom = db_column_text(&q, 3); |
| 495 | 496 | const char *zBr = db_column_text(&q, 7); |
| 496 | - char *z; | |
| 497 | + char *z = 0; | |
| 498 | + char * zBranchPoint = 0; | |
| 497 | 499 | |
| 498 | 500 | if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ |
| 499 | 501 | fossil_print("*** %s ***\n", zBr); |
| 500 | 502 | fossil_free(zLastBr); |
| 501 | 503 | zLastBr = fossil_strdup(zBr); |
| @@ -502,14 +504,27 @@ | ||
| 502 | 504 | if( multipleFlag ) n = 0; |
| 503 | 505 | } |
| 504 | 506 | n++; |
| 505 | 507 | sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); |
| 506 | 508 | 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 : ""); | |
| 508 | 521 | comment_print(z, zCom, 7, width, get_comment_format()); |
| 509 | 522 | fossil_free(z); |
| 523 | + fossil_free(zBranchPoint); | |
| 510 | 524 | } |
| 525 | + fossil_free(zMainBranch); | |
| 511 | 526 | fossil_free(zLastBr); |
| 512 | 527 | db_finalize(&q); |
| 513 | 528 | } |
| 514 | 529 | |
| 515 | 530 | /* |
| @@ -565,11 +580,10 @@ | ||
| 565 | 580 | url_reset(&url); |
| 566 | 581 | style_set_current_feature("leaves"); |
| 567 | 582 | style_header("Leaves"); |
| 568 | 583 | login_anonymous_available(); |
| 569 | 584 | timeline_ss_submenu(); |
| 570 | - cookie_render(); | |
| 571 | 585 | #if 0 |
| 572 | 586 | style_sidebox_begin("Nomenclature:", "33%"); |
| 573 | 587 | @ <ol> |
| 574 | 588 | @ <li> A <div class="sideboxDescribed">leaf</div> |
| 575 | 589 | @ is a check-in with no descendants in the same branch.</li> |
| 576 | 590 |
| --- 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 @@ | ||
| 1010 | 1010 | @ --sqlstats Show SQL usage statistics when done |
| 1011 | 1011 | @ --sqltrace Trace all SQL commands |
| 1012 | 1012 | @ --sshtrace Trace SSH activity |
| 1013 | 1013 | @ --ssl-identity NAME Set the SSL identity to NAME |
| 1014 | 1014 | @ --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 | |
| 1016 | 1016 | @ --utc Display times using UTC |
| 1017 | 1017 | @ --vfs NAME Cause SQLite to use the NAME VFS |
| 1018 | 1018 | ; |
| 1019 | 1019 | |
| 1020 | 1020 | /* |
| 1021 | 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 | @ --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 @@ | ||
| 73 | 73 | return "unknown/unknown"; |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | 76 | /* A table of mimetypes based on file suffixes. |
| 77 | 77 | ** 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. | |
| 79 | 79 | */ |
| 80 | 80 | static const struct { |
| 81 | 81 | const char *zSuffix; /* The file suffix */ |
| 82 | 82 | int size; /* Length of the suffix */ |
| 83 | 83 | const char *zMimetype; /* The corresponding mimetype */ |
| @@ -427,11 +427,11 @@ | ||
| 427 | 427 | style_script_end(); |
| 428 | 428 | } |
| 429 | 429 | } |
| 430 | 430 | |
| 431 | 431 | /* |
| 432 | -** Guess the mime-type of a document based on its name. | |
| 432 | +** Guess the mimetype of a document based on its name. | |
| 433 | 433 | */ |
| 434 | 434 | const char *mimetype_from_name(const char *zName){ |
| 435 | 435 | const char *z; |
| 436 | 436 | int i; |
| 437 | 437 | int first, last; |
| 438 | 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 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 @@ | ||
| 478 | 478 | ** |
| 479 | 479 | ** If the "--export-marks FILE" option is used, the rid of all commits and |
| 480 | 480 | ** blobs written on exit for use with "--import-marks" on the next run. |
| 481 | 481 | ** |
| 482 | 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 | |
| 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 | 487 | ** |
| 488 | 488 | ** See also: import |
| 489 | 489 | */ |
| 490 | 490 | /* |
| 491 | 491 | ** COMMAND: export* |
| 492 | 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 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 @@ | ||
| 753 | 753 | int newRid = 0; /* RID of new version */ |
| 754 | 754 | const char * zFilename; /* argv[2] */ |
| 755 | 755 | const char * zComment; /* -m comment */ |
| 756 | 756 | const char * zCommentFile; /* -M FILE */ |
| 757 | 757 | const char * zAsFilename; /* --as filename */ |
| 758 | - const char * zRevision; /* --revision|-r [=trunk|checkout] */ | |
| 758 | + const char * zRevision; /* -r|--revision [=trunk|checkout] */ | |
| 759 | 759 | const char * zUser; /* --user-override */ |
| 760 | 760 | const char * zDate; /* --date-override */ |
| 761 | 761 | char const * zManifestFile = 0;/* --save-manifest FILE */ |
| 762 | 762 | |
| 763 | 763 | /* This function should perform only the minimal "business logic" it |
| 764 | 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; /* --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 @@ | ||
| 39 | 39 | ** In the -p mode, there's an optional flag "-r|--revision REVISION". |
| 40 | 40 | ** The specified version (or the latest checked out version) is printed |
| 41 | 41 | ** to stdout. The -p mode is another form of the "cat" command. |
| 42 | 42 | ** |
| 43 | 43 | ** Options: |
| 44 | -** -b|--brief display a brief (one line / revision) summary | |
| 44 | +** -b|--brief Display a brief (one line / revision) summary | |
| 45 | 45 | ** --case-sensitive B Enable or disable case-sensitive filenames. B is a |
| 46 | 46 | ** boolean: "yes", "no", "true", "false", etc. |
| 47 | -** -l|--log select log mode (the default) | |
| 47 | +** -l|--log Select log mode (the default) | |
| 48 | 48 | ** -n|--limit N Display the first N changes (default unlimited). |
| 49 | 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) | |
| 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 | 53 | ** 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) | |
| 55 | 55 | ** -W|--width N Width of lines (default is to auto-detect). Must be |
| 56 | 56 | ** more than 22 or else 0 to indicate no limit. |
| 57 | 57 | ** |
| 58 | 58 | ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]] |
| 59 | 59 | */ |
| @@ -241,11 +241,11 @@ | ||
| 241 | 241 | ** Print on standard output the content of one or more files as they exist |
| 242 | 242 | ** in the repository. The version currently checked out is shown by default. |
| 243 | 243 | ** Other versions may be specified using the -r option. |
| 244 | 244 | ** |
| 245 | 245 | ** Options: |
| 246 | -** -R|--repository FILE Extract artifacts from repository FILE | |
| 246 | +** -R|--repository REPO Extract artifacts from repository REPO | |
| 247 | 247 | ** -r VERSION The specific check-in containing the file |
| 248 | 248 | ** |
| 249 | 249 | ** See also: [[finfo]] |
| 250 | 250 | */ |
| 251 | 251 | void cat_cmd(void){ |
| @@ -364,11 +364,10 @@ | ||
| 364 | 364 | url_initialize(&url, "finfo"); |
| 365 | 365 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 366 | 366 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 367 | 367 | ridFrom = name_to_rid_www("from"); |
| 368 | 368 | zPrevDate[0] = 0; |
| 369 | - cookie_render(); | |
| 370 | 369 | if( fnid==0 ){ |
| 371 | 370 | @ No such file: %h(zFilename) |
| 372 | 371 | style_finish_page(); |
| 373 | 372 | return; |
| 374 | 373 | } |
| 375 | 374 | |
| 376 | 375 | 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 @@ | ||
| 104 | 104 | const char *zAuxFilename /* Name of auxiliary information file */ |
| 105 | 105 | ){ |
| 106 | 106 | Blob r; |
| 107 | 107 | int i; |
| 108 | 108 | blob_init(&r, 0, 0); |
| 109 | + if( zCmd==0 ) return 0; | |
| 109 | 110 | while( zCmd[0] ){ |
| 110 | 111 | for(i=0; zCmd[i] && zCmd[i]!='%'; i++){} |
| 111 | 112 | blob_append(&r, zCmd, i); |
| 112 | 113 | if( zCmd[i]==0 ) break; |
| 113 | 114 | if( zCmd[i+1]=='F' ){ |
| @@ -403,10 +404,11 @@ | ||
| 403 | 404 | while( db_step(&q)==SQLITE_ROW ){ |
| 404 | 405 | const char *zCmd = db_column_text(&q,0); |
| 405 | 406 | char *zCmd2 = hook_subst(zCmd, zAuxFilename); |
| 406 | 407 | int needOut = db_column_int(&q,1); |
| 407 | 408 | Blob out; |
| 409 | + if( zCmd2==0 ) continue; | |
| 408 | 410 | blob_init(&out,0,0); |
| 409 | 411 | if( needOut ) hook_changes(&out, zOrigRcvid, zNewRcvid); |
| 410 | 412 | if( bDryRun ){ |
| 411 | 413 | fossil_print("%s\n", zCmd2); |
| 412 | 414 | if( needOut ){ |
| 413 | 415 |
| --- 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 @@ | ||
| 188 | 188 | ** Use the "finfo" command to get information about a specific |
| 189 | 189 | ** file in a checkout. |
| 190 | 190 | ** |
| 191 | 191 | ** Options: |
| 192 | 192 | ** |
| 193 | -** -R|--repository FILE Extract info from repository FILE | |
| 193 | +** -R|--repository REPO Extract info from repository REPO | |
| 194 | 194 | ** -v|--verbose Show extra information about repositories |
| 195 | 195 | ** |
| 196 | 196 | ** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]] |
| 197 | 197 | */ |
| 198 | 198 | void info_cmd(void){ |
| @@ -657,12 +657,11 @@ | ||
| 657 | 657 | " AND event.objid=%d", |
| 658 | 658 | rid, rid |
| 659 | 659 | ); |
| 660 | 660 | zBrName = branch_of_rid(rid); |
| 661 | 661 | |
| 662 | - cookie_link_parameter("diff","diff","2"); | |
| 663 | - diffType = atoi(PD("diff","2")); | |
| 662 | + diffType = preferred_diff_type(); | |
| 664 | 663 | if( db_step(&q1)==SQLITE_ROW ){ |
| 665 | 664 | const char *zUuid = db_column_text(&q1, 0); |
| 666 | 665 | int nUuid = db_column_bytes(&q1, 0); |
| 667 | 666 | char *zEUser, *zEComment; |
| 668 | 667 | const char *zUser; |
| @@ -940,11 +939,11 @@ | ||
| 940 | 939 | append_file_change_line(zUuid, zName, zOld, zNew, zOldName, |
| 941 | 940 | diffFlags,pRe,mperm); |
| 942 | 941 | } |
| 943 | 942 | db_finalize(&q3); |
| 944 | 943 | append_diff_javascript(diffType==2); |
| 945 | - cookie_render(); | |
| 944 | + builtin_fossil_js_bundle_or("info-diff",NULL); | |
| 946 | 945 | style_finish_page(); |
| 947 | 946 | } |
| 948 | 947 | |
| 949 | 948 | /* |
| 950 | 949 | ** WEBPAGE: winfo |
| @@ -1185,13 +1184,11 @@ | ||
| 1185 | 1184 | ReCompiled *pRe = 0; |
| 1186 | 1185 | login_check_credentials(); |
| 1187 | 1186 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1188 | 1187 | login_anonymous_available(); |
| 1189 | 1188 | load_control(); |
| 1190 | - cookie_link_parameter("diff","diff","2"); | |
| 1191 | - diffType = atoi(PD("diff","2")); | |
| 1192 | - cookie_render(); | |
| 1189 | + diffType = preferred_diff_type(); | |
| 1193 | 1190 | zRe = P("regex"); |
| 1194 | 1191 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1195 | 1192 | zBranch = P("branch"); |
| 1196 | 1193 | if( zBranch && zBranch[0]==0 ) zBranch = 0; |
| 1197 | 1194 | if( zBranch ){ |
| @@ -1330,10 +1327,11 @@ | ||
| 1330 | 1327 | } |
| 1331 | 1328 | } |
| 1332 | 1329 | manifest_destroy(pFrom); |
| 1333 | 1330 | manifest_destroy(pTo); |
| 1334 | 1331 | append_diff_javascript(diffType==2); |
| 1332 | + builtin_fossil_js_bundle_or("info-diff",NULL); | |
| 1335 | 1333 | style_finish_page(); |
| 1336 | 1334 | } |
| 1337 | 1335 | |
| 1338 | 1336 | #if INTERFACE |
| 1339 | 1337 | /* |
| @@ -1634,10 +1632,49 @@ | ||
| 1634 | 1632 | tag_private_status(rid); |
| 1635 | 1633 | } |
| 1636 | 1634 | return objType; |
| 1637 | 1635 | } |
| 1638 | 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 | + | |
| 1639 | 1676 | |
| 1640 | 1677 | /* |
| 1641 | 1678 | ** WEBPAGE: fdiff |
| 1642 | 1679 | ** URL: fdiff?v1=HASH&v2=HASH |
| 1643 | 1680 | ** |
| @@ -1673,13 +1710,11 @@ | ||
| 1673 | 1710 | u32 objdescFlags = 0; |
| 1674 | 1711 | int verbose = PB("verbose"); |
| 1675 | 1712 | |
| 1676 | 1713 | login_check_credentials(); |
| 1677 | 1714 | 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(); | |
| 1681 | 1716 | if( P("from") && P("to") ){ |
| 1682 | 1717 | v1 = artifact_from_ci_and_filename("from"); |
| 1683 | 1718 | v2 = artifact_from_ci_and_filename("to"); |
| 1684 | 1719 | }else{ |
| 1685 | 1720 | Stmt q; |
| @@ -1832,12 +1867,12 @@ | ||
| 1832 | 1867 | } |
| 1833 | 1868 | |
| 1834 | 1869 | |
| 1835 | 1870 | /* |
| 1836 | 1871 | ** 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 | |
| 1839 | 1874 | ** associated with the artifact. |
| 1840 | 1875 | */ |
| 1841 | 1876 | void deliver_artifact(int rid, const char *zMime){ |
| 1842 | 1877 | Blob content; |
| 1843 | 1878 | const char *zAttachName = P("at"); |
| 1844 | 1879 |
| --- 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 |
+2
-2
| --- src/json_branch.c | ||
| +++ src/json_branch.c | ||
| @@ -50,12 +50,12 @@ | ||
| 50 | 50 | ** Impl for /json/branch/list |
| 51 | 51 | ** |
| 52 | 52 | ** |
| 53 | 53 | ** CLI mode options: |
| 54 | 54 | ** |
| 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) | |
| 57 | 57 | ** -a (same as --range a) |
| 58 | 58 | ** -c (same as --range c) |
| 59 | 59 | ** |
| 60 | 60 | ** HTTP mode options: |
| 61 | 61 | ** |
| 62 | 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 | ** --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 @@ | ||
| 426 | 426 | if( strncmp(zAgent, "Safari/", 7)==0 ) return 1; |
| 427 | 427 | if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1; |
| 428 | 428 | if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1; |
| 429 | 429 | return 0; |
| 430 | 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 | +} | |
| 431 | 449 | |
| 432 | 450 | /* |
| 433 | 451 | ** COMMAND: test-ishuman |
| 434 | 452 | ** |
| 435 | 453 | ** Read lines of text from standard input. Interpret each line of text |
| 436 | 454 |
| --- 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 @@ | ||
| 401 | 401 | ** |
| 402 | 402 | ** Usage: %fossil test-looks-like-utf FILENAME |
| 403 | 403 | ** |
| 404 | 404 | ** Options: |
| 405 | 405 | ** -n|--limit N Repeat looks-like function N times, for |
| 406 | -** performance measurement. Default = 1; | |
| 406 | +** performance measurement. Default = 1 | |
| 407 | 407 | ** --utf8 Ignoring BOM and file size, force UTF-8 checking |
| 408 | 408 | ** --utf16 Ignoring BOM and file size, force UTF-16 checking |
| 409 | 409 | ** |
| 410 | 410 | ** FILENAME is the name of a file to check for textual content in the UTF-8 |
| 411 | 411 | ** and/or UTF-16 encodings. |
| 412 | 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 |
| --- 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 @@ | ||
| 679 | 679 | } |
| 680 | 680 | #endif |
| 681 | 681 | |
| 682 | 682 | fossil_printf_selfcheck(); |
| 683 | 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! */ | |
| 684 | 688 | if( sqlite3_libversion_number()<3035000 ){ |
| 685 | 689 | fossil_panic("Unsuitable SQLite version %s, must be at least 3.35.0", |
| 686 | 690 | sqlite3_libversion()); |
| 687 | 691 | } |
| 692 | + | |
| 688 | 693 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| 689 | 694 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| 690 | 695 | memset(&g, 0, sizeof(g)); |
| 691 | 696 | g.now = time(0); |
| 692 | 697 | g.httpHeader = empty_blob; |
| @@ -1838,33 +1843,11 @@ | ||
| 1838 | 1843 | zPathInfo += 7; |
| 1839 | 1844 | g.nExtraURL += 7; |
| 1840 | 1845 | cgi_replace_parameter("PATH_INFO", zPathInfo); |
| 1841 | 1846 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1842 | 1847 | 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 | + } | |
| 1866 | 1849 | |
| 1867 | 1850 | /* If the content type is application/x-fossil or |
| 1868 | 1851 | ** application/x-fossil-debug, then a sync/push/pull/clone is |
| 1869 | 1852 | ** desired, so default the PATH_INFO to /xfer |
| 1870 | 1853 | */ |
| @@ -2182,11 +2165,11 @@ | ||
| 2182 | 2165 | ** setenv: NAME VALUE Set environment variable NAME to VALUE. Or |
| 2183 | 2166 | ** if VALUE is omitted, unset NAME. |
| 2184 | 2167 | ** |
| 2185 | 2168 | ** HOME: PATH Shorthand for "setenv: HOME PATH" |
| 2186 | 2169 | ** |
| 2187 | -** debug: FILE Causing debugging information to be written | |
| 2170 | +** cgi-debug: FILE Causing debugging information to be written | |
| 2188 | 2171 | ** into FILE. |
| 2189 | 2172 | ** |
| 2190 | 2173 | ** errorlog: FILE Warnings, errors, and panics written to FILE. |
| 2191 | 2174 | ** |
| 2192 | 2175 | ** timeout: SECONDS Do not run for longer than SECONDS. The default |
| @@ -2378,11 +2361,11 @@ | ||
| 2378 | 2361 | ** Use one of the built-in skins defined by LABEL. LABEL is the |
| 2379 | 2362 | ** name of the subdirectory under the skins/ directory that holds |
| 2380 | 2363 | ** the elements of the built-in skin. If LABEL does not match, |
| 2381 | 2364 | ** this directive is a silent no-op. |
| 2382 | 2365 | */ |
| 2383 | - skin_use_alternative(blob_str(&value)); | |
| 2366 | + fossil_free(skin_use_alternative(blob_str(&value), 1)); | |
| 2384 | 2367 | blob_reset(&value); |
| 2385 | 2368 | continue; |
| 2386 | 2369 | } |
| 2387 | 2370 | if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){ |
| 2388 | 2371 | /* jsmode: MODE |
| @@ -2737,12 +2720,12 @@ | ||
| 2737 | 2720 | ** COMMAND: test-http |
| 2738 | 2721 | ** |
| 2739 | 2722 | ** Works like the [[http]] command but gives setup permission to all users. |
| 2740 | 2723 | ** |
| 2741 | 2724 | ** 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") | |
| 2744 | 2727 | ** |
| 2745 | 2728 | */ |
| 2746 | 2729 | void cmd_test_http(void){ |
| 2747 | 2730 | const char *zIpAddr; /* IP address of remote client */ |
| 2748 | 2731 | const char *zUserCap; |
| 2749 | 2732 |
| --- 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 |
+1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -225,10 +225,11 @@ | ||
| 225 | 225 | $(SRCDIR)/fossil.bootstrap.js \ |
| 226 | 226 | $(SRCDIR)/fossil.confirmer.js \ |
| 227 | 227 | $(SRCDIR)/fossil.copybutton.js \ |
| 228 | 228 | $(SRCDIR)/fossil.dom.js \ |
| 229 | 229 | $(SRCDIR)/fossil.fetch.js \ |
| 230 | + $(SRCDIR)/fossil.info-diff.js \ | |
| 230 | 231 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 231 | 232 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 232 | 233 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 233 | 234 | $(SRCDIR)/fossil.page.pikchrshow.js \ |
| 234 | 235 | $(SRCDIR)/fossil.page.whistory.js \ |
| 235 | 236 |
| --- 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
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -225,10 +225,11 @@ | ||
| 225 | 225 | $(SRCDIR)/fossil.bootstrap.js \ |
| 226 | 226 | $(SRCDIR)/fossil.confirmer.js \ |
| 227 | 227 | $(SRCDIR)/fossil.copybutton.js \ |
| 228 | 228 | $(SRCDIR)/fossil.dom.js \ |
| 229 | 229 | $(SRCDIR)/fossil.fetch.js \ |
| 230 | + $(SRCDIR)/fossil.info-diff.js \ | |
| 230 | 231 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 231 | 232 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 232 | 233 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 233 | 234 | $(SRCDIR)/fossil.page.pikchrshow.js \ |
| 234 | 235 | $(SRCDIR)/fossil.page.whistory.js \ |
| 235 | 236 |
| --- 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 @@ | ||
| 1281 | 1281 | ** repositories after making any changes to the manifest_parse() |
| 1282 | 1282 | ** implementation to confirm that the changes did not break anything. |
| 1283 | 1283 | ** |
| 1284 | 1284 | ** Options: |
| 1285 | 1285 | ** |
| 1286 | -** --limit N Parse no more than N artifacts before stopping. | |
| 1286 | +** --limit N Parse no more than N artifacts before stopping | |
| 1287 | 1287 | ** --wellformed Use all BLOB table entries as input, not just |
| 1288 | 1288 | ** those entries that are believed to be valid |
| 1289 | 1289 | ** artifacts, and verify that the result the |
| 1290 | 1290 | ** manifest_is_well_formed() agrees with the |
| 1291 | 1291 | ** result of manifest_parse(). |
| 1292 | 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/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 |
+1
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -317,10 +317,11 @@ | ||
| 317 | 317 | nConflict++; |
| 318 | 318 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 319 | 319 | sz++; |
| 320 | 320 | } |
| 321 | 321 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 322 | + ensure_line_end(pOut, useCrLf); | |
| 322 | 323 | blob_append(pOut, mergeMarker[0], -1); |
| 323 | 324 | ensure_line_end(pOut, useCrLf); |
| 324 | 325 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 325 | 326 | ensure_line_end(pOut, useCrLf); |
| 326 | 327 | blob_append(pOut, mergeMarker[1], -1); |
| 327 | 328 |
| --- 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 @@ | ||
| 909 | 909 | ** plays. |
| 910 | 910 | ** |
| 911 | 911 | ** Options: |
| 912 | 912 | ** |
| 913 | 913 | ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't', |
| 914 | -** 'w', 'g', or 'e'). | |
| 914 | +** 'w', 'g', or 'e') | |
| 915 | 915 | ** -v|--verbose Provide extra information (such as the RID) |
| 916 | 916 | */ |
| 917 | 917 | void whatis_cmd(void){ |
| 918 | 918 | int rid; |
| 919 | 919 | const char *zName; |
| 920 | 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 |
| --- 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 @@ | ||
| 389 | 389 | ** resp. stdout, and the names "-" can be used as aliases for those |
| 390 | 390 | ** streams. |
| 391 | 391 | ** |
| 392 | 392 | ** Options: |
| 393 | 393 | ** |
| 394 | -** -div On success, adds a DIV wrapper around the | |
| 394 | +** -div On success, add a DIV wrapper around the | |
| 395 | 395 | ** resulting SVG output which limits its max-width to |
| 396 | 396 | ** its computed maximum ideal size. |
| 397 | 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 | |
| 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 | 407 | ** JavaScript-side post-processor). |
| 408 | 408 | ** |
| 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 | |
| 410 | 410 | ** CSS to hide the SVG and reveal the source by default. |
| 411 | 411 | ** |
| 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 | |
| 413 | 413 | ** a separate element adjacent to the SVG one. Implied |
| 414 | 414 | ** by -div-source. |
| 415 | 415 | ** |
| 416 | 416 | ** |
| 417 | 417 | ** -th Process the input using TH1 before passing it to pikchr. |
| 418 | 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, 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 @@ | ||
| 505 | 505 | ** |
| 506 | 506 | ** TBD... |
| 507 | 507 | ** |
| 508 | 508 | ** COMMON OPTIONS: |
| 509 | 509 | ** |
| 510 | -** --explain Make no changes, but show what would happen. | |
| 510 | +** --explain Make no changes, but show what would happen | |
| 511 | 511 | ** --dry-run An alias for --explain |
| 512 | 512 | */ |
| 513 | 513 | void purge_cmd(void){ |
| 514 | 514 | int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; |
| 515 | 515 | const char *zSubcmd; |
| 516 | 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 |
| --- 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 @@ | ||
| 889 | 889 | ** |
| 890 | 890 | ** The user is prompted to confirm the scrub unless the --force option |
| 891 | 891 | ** is used. |
| 892 | 892 | ** |
| 893 | 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) | |
| 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 | 897 | */ |
| 898 | 898 | void scrub_cmd(void){ |
| 899 | 899 | int bVerily = find_option("verily",0,0)!=0; |
| 900 | 900 | int bForce = find_option("force", "f", 0)!=0; |
| 901 | 901 | int privateOnly = find_option("private",0,0)!=0; |
| @@ -1114,11 +1114,11 @@ | ||
| 1114 | 1114 | ** |
| 1115 | 1115 | ** No files or directories will be created. |
| 1116 | 1116 | ** |
| 1117 | 1117 | ** Options: |
| 1118 | 1118 | ** -L|--prefixlength N Set the length of the names of the DESTINATION |
| 1119 | -** subdirectories to N. | |
| 1119 | +** subdirectories to N | |
| 1120 | 1120 | */ |
| 1121 | 1121 | void test_hash_from_path_cmd(void) { |
| 1122 | 1122 | char *zDest; |
| 1123 | 1123 | char *zUuid; |
| 1124 | 1124 | char *zFile; |
| @@ -1284,11 +1284,11 @@ | ||
| 1284 | 1284 | ** 40+ character artifact ID, AA the first 2 characters. |
| 1285 | 1285 | ** If -L|--prefixlength is given, the length (default 2) of the directory prefix |
| 1286 | 1286 | ** can be set to 0,1,..,9 characters. |
| 1287 | 1287 | ** |
| 1288 | 1288 | ** Options: |
| 1289 | -** -R|--repository REPOSITORY Deconstruct given REPOSITORY. | |
| 1289 | +** -R|--repository REPO Deconstruct given REPOSITORY. | |
| 1290 | 1290 | ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to |
| 1291 | 1291 | ** the file .rid1 in the DESTINATION directory. |
| 1292 | 1292 | ** -L|--prefixlength N Set the length of the names of the DESTINATION |
| 1293 | 1293 | ** subdirectories to N. |
| 1294 | 1294 | ** --private Include private artifacts. |
| 1295 | 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 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 @@ | ||
| 224 | 224 | ** Usage: %fossil rss ?OPTIONS? |
| 225 | 225 | ** |
| 226 | 226 | ** The CLI variant of the /timeline.rss page, this produces an RSS |
| 227 | 227 | ** feed of the timeline to stdout. Options: |
| 228 | 228 | ** |
| 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), | |
| 230 | 230 | ** t (show tickets only), w (show wiki only). |
| 231 | 231 | ** |
| 232 | 232 | ** -limit|n LIMIT The maximum number of items to show. |
| 233 | 233 | ** |
| 234 | -** -tkt HASH Filters for only those events for the specified ticket. | |
| 234 | +** -tkt HASH Filter for only those events for the specified ticket. | |
| 235 | 235 | ** |
| 236 | -** -tag TAG filters for a tag | |
| 236 | +** -tag TAG Filter for a tag | |
| 237 | 237 | ** |
| 238 | -** -wiki NAME Filters on a specific wiki page. | |
| 238 | +** -wiki NAME Filter on a specific wiki page. | |
| 239 | 239 | ** |
| 240 | 240 | ** Only one of -tkt, -tag, or -wiki may be used. |
| 241 | 241 | ** |
| 242 | -** -name FILENAME filters for a specific file. This may be combined | |
| 242 | +** -name FILENAME Filter for a specific file. This may be combined | |
| 243 | 243 | ** with one of the other filters (useful for looking |
| 244 | 244 | ** at a specific branch). |
| 245 | 245 | ** |
| 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. | |
| 247 | 247 | ** The default is "URL-PLACEHOLDER" (without quotes). |
| 248 | 248 | */ |
| 249 | 249 | void cmd_timeline_rss(void){ |
| 250 | 250 | Stmt q; |
| 251 | 251 | int nLine=0; |
| 252 | 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 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 @@ | ||
| 952 | 952 | @ </td></tr></table> |
| 953 | 953 | @ </div></form> |
| 954 | 954 | db_end_transaction(0); |
| 955 | 955 | style_finish_page(); |
| 956 | 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 | + | |
| 957 | 1009 | |
| 958 | 1010 | /* |
| 959 | 1011 | ** WEBPAGE: setup_config |
| 960 | 1012 | ** |
| 961 | 1013 | ** The "Admin/Configuration" page. Requires Setup privilege. |
| @@ -1026,11 +1078,11 @@ | ||
| 1026 | 1078 | @ leading "/".</p> |
| 1027 | 1079 | @ <p>(Property: "index-page") |
| 1028 | 1080 | @ <hr> |
| 1029 | 1081 | @ <p>The main menu for the web interface |
| 1030 | 1082 | @ <p> |
| 1031 | - @ | |
| 1083 | + @ | |
| 1032 | 1084 | @ <p>This setting should be a TCL list. Each set of four consecutive |
| 1033 | 1085 | @ values defines a single main menu item: |
| 1034 | 1086 | @ <ol> |
| 1035 | 1087 | @ <li> The first term is text that appears on the menu. |
| 1036 | 1088 | @ <li> The second term is a hyperlink to take when a user clicks on the |
| @@ -1043,12 +1095,12 @@ | ||
| 1043 | 1095 | @ menu entry. Some skins will classes "desktoponly" and "wideonly" |
| 1044 | 1096 | @ to only show the entries when the web browser screen is wide or |
| 1045 | 1097 | @ very wide, respectively. |
| 1046 | 1098 | @ </ol> |
| 1047 | 1099 | @ |
| 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 | |
| 1050 | 1102 | @ choices (such as the hamburger button) to the menu that are not shown |
| 1051 | 1103 | @ on this list. (Property: mainmenu) |
| 1052 | 1104 | @ <p> |
| 1053 | 1105 | if(P("resetMenu")!=0){ |
| 1054 | 1106 | db_unset("mainmenu", 0); |
| 1055 | 1107 |
| --- 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 @@ | ||
| 503 | 503 | ** |
| 504 | 504 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 505 | 505 | ** If a file is named "-" then take its content from standard input. |
| 506 | 506 | ** Options: |
| 507 | 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. | |
| 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 | 512 | ** |
| 513 | 513 | ** See also: [[md5sum]], [[sha3sum]] |
| 514 | 514 | */ |
| 515 | 515 | void sha1sum_test(void){ |
| 516 | 516 | int i; |
| 517 | 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 |
| --- 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 @@ | ||
| 635 | 635 | ** --256 Compute a SHA3-256 hash (the default) |
| 636 | 636 | ** --384 Compute a SHA3-384 hash |
| 637 | 637 | ** --512 Compute a SHA3-512 hash |
| 638 | 638 | ** --size N An N-bit hash. N must be a multiple of 32 between |
| 639 | 639 | ** 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 | |
| 641 | 641 | ** the object pointed to, not on the link itself. |
| 642 | 642 | ** |
| 643 | 643 | ** See also: [[md5sum]], [[sha1sum]] |
| 644 | 644 | */ |
| 645 | 645 | void sha3sum_test(void){ |
| 646 | 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 |
| --- 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 @@ | ||
| 3638 | 3638 | ** |
| 3639 | 3639 | ** This file implements a VFS shim that allows an SQLite database to be |
| 3640 | 3640 | ** appended onto the end of some other file, such as an executable. |
| 3641 | 3641 | ** |
| 3642 | 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. | |
| 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. | |
| 3646 | 3647 | ** |
| 3647 | 3648 | ** 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. | |
| 3661 | 3660 | ** |
| 3662 | 3661 | ** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is |
| 3663 | 3662 | ** set, then a new database is appended to the already existing file. |
| 3664 | 3663 | ** |
| 3665 | 3664 | ** (5) Otherwise, SQLITE_CANTOPEN is returned. |
| 3666 | 3665 | ** |
| 3667 | 3666 | ** 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. | |
| 3672 | 3671 | ** |
| 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) | |
| 3675 | 3674 | **/ |
| 3676 | 3675 | /* #include "sqlite3ext.h" */ |
| 3677 | 3676 | SQLITE_EXTENSION_INIT1 |
| 3678 | 3677 | #include <string.h> |
| 3679 | 3678 | #include <assert.h> |
| @@ -3682,22 +3681,30 @@ | ||
| 3682 | 3681 | ** |
| 3683 | 3682 | ** Start-Of-SQLite3-NNNNNNNN |
| 3684 | 3683 | ** 123456789 123456789 12345 |
| 3685 | 3684 | ** |
| 3686 | 3685 | ** 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. | |
| 3688 | 3687 | */ |
| 3689 | 3688 | #define APND_MARK_PREFIX "Start-Of-SQLite3-" |
| 3690 | 3689 | #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) | |
| 3692 | 3692 | |
| 3693 | 3693 | /* |
| 3694 | 3694 | ** Maximum size of the combined prefix + database + append-mark. This |
| 3695 | 3695 | ** must be less than 0x40000000 to avoid locking issues on Windows. |
| 3696 | 3696 | */ |
| 3697 | 3697 | #define APND_MAX_SIZE (65536*15259) |
| 3698 | 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 | + | |
| 3699 | 3706 | /* |
| 3700 | 3707 | ** Forward declaration of objects used by this utility |
| 3701 | 3708 | */ |
| 3702 | 3709 | typedef struct sqlite3_vfs ApndVfs; |
| 3703 | 3710 | typedef struct ApndFile ApndFile; |
| @@ -3706,15 +3713,43 @@ | ||
| 3706 | 3713 | ** access to randomness, etc. |
| 3707 | 3714 | */ |
| 3708 | 3715 | #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) |
| 3709 | 3716 | #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) |
| 3710 | 3717 | |
| 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 | + */ | |
| 3712 | 3744 | 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; | |
| 3716 | 3751 | }; |
| 3717 | 3752 | |
| 3718 | 3753 | /* |
| 3719 | 3754 | ** Methods for ApndFile |
| 3720 | 3755 | */ |
| @@ -3802,12 +3837,10 @@ | ||
| 3802 | 3837 | apndShmUnmap, /* xShmUnmap */ |
| 3803 | 3838 | apndFetch, /* xFetch */ |
| 3804 | 3839 | apndUnfetch /* xUnfetch */ |
| 3805 | 3840 | }; |
| 3806 | 3841 | |
| 3807 | - | |
| 3808 | - | |
| 3809 | 3842 | /* |
| 3810 | 3843 | ** Close an apnd-file. |
| 3811 | 3844 | */ |
| 3812 | 3845 | static int apndClose(sqlite3_file *pFile){ |
| 3813 | 3846 | pFile = ORIGFILE(pFile); |
| @@ -3821,26 +3854,41 @@ | ||
| 3821 | 3854 | sqlite3_file *pFile, |
| 3822 | 3855 | void *zBuf, |
| 3823 | 3856 | int iAmt, |
| 3824 | 3857 | sqlite_int64 iOfst |
| 3825 | 3858 | ){ |
| 3826 | - ApndFile *p = (ApndFile *)pFile; | |
| 3859 | + ApndFile *paf = (ApndFile *)pFile; | |
| 3827 | 3860 | 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); | |
| 3829 | 3862 | } |
| 3830 | 3863 | |
| 3831 | 3864 | /* |
| 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. | |
| 3833 | 3868 | */ |
| 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; | |
| 3836 | 3875 | unsigned char a[APND_MARK_SIZE]; |
| 3876 | + int i = APND_MARK_FOS_SZ; | |
| 3877 | + int rc; | |
| 3878 | + assert(pFile == ORIGFILE(paf)); | |
| 3837 | 3879 | 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; | |
| 3840 | 3883 | } |
| 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; | |
| 3842 | 3890 | } |
| 3843 | 3891 | |
| 3844 | 3892 | /* |
| 3845 | 3893 | ** Write data to an apnd-file. |
| 3846 | 3894 | */ |
| @@ -3848,42 +3896,34 @@ | ||
| 3848 | 3896 | sqlite3_file *pFile, |
| 3849 | 3897 | const void *zBuf, |
| 3850 | 3898 | int iAmt, |
| 3851 | 3899 | sqlite_int64 iOfst |
| 3852 | 3900 | ){ |
| 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; | |
| 3855 | 3904 | 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); | |
| 3870 | 3912 | } |
| 3871 | 3913 | |
| 3872 | 3914 | /* |
| 3873 | 3915 | ** Truncate an apnd-file. |
| 3874 | 3916 | */ |
| 3875 | 3917 | static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
| 3876 | - int rc; | |
| 3877 | - ApndFile *p = (ApndFile *)pFile; | |
| 3918 | + ApndFile *paf = (ApndFile *)pFile; | |
| 3878 | 3919 | 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); | |
| 3885 | 3925 | } |
| 3886 | 3926 | |
| 3887 | 3927 | /* |
| 3888 | 3928 | ** Sync an apnd-file. |
| 3889 | 3929 | */ |
| @@ -3892,20 +3932,16 @@ | ||
| 3892 | 3932 | return pFile->pMethods->xSync(pFile, flags); |
| 3893 | 3933 | } |
| 3894 | 3934 | |
| 3895 | 3935 | /* |
| 3896 | 3936 | ** Return the current file-size of an apnd-file. |
| 3937 | +** If the append mark is not yet there, the file-size is 0. | |
| 3897 | 3938 | */ |
| 3898 | 3939 | 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; | |
| 3907 | 3943 | } |
| 3908 | 3944 | |
| 3909 | 3945 | /* |
| 3910 | 3946 | ** Lock an apnd-file. |
| 3911 | 3947 | */ |
| @@ -3932,16 +3968,17 @@ | ||
| 3932 | 3968 | |
| 3933 | 3969 | /* |
| 3934 | 3970 | ** File control method. For custom operations on an apnd-file. |
| 3935 | 3971 | */ |
| 3936 | 3972 | static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){ |
| 3937 | - ApndFile *p = (ApndFile *)pFile; | |
| 3973 | + ApndFile *paf = (ApndFile *)pFile; | |
| 3938 | 3974 | int rc; |
| 3939 | 3975 | pFile = ORIGFILE(pFile); |
| 3976 | + if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne; | |
| 3940 | 3977 | rc = pFile->pMethods->xFileControl(pFile, op, pArg); |
| 3941 | 3978 | 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); | |
| 3943 | 3980 | } |
| 3944 | 3981 | return rc; |
| 3945 | 3982 | } |
| 3946 | 3983 | |
| 3947 | 3984 | /* |
| @@ -3996,10 +4033,12 @@ | ||
| 3996 | 4033 | sqlite3_int64 iOfst, |
| 3997 | 4034 | int iAmt, |
| 3998 | 4035 | void **pp |
| 3999 | 4036 | ){ |
| 4000 | 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. */ | |
| 4001 | 4040 | pFile = ORIGFILE(pFile); |
| 4002 | 4041 | return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); |
| 4003 | 4042 | } |
| 4004 | 4043 | |
| 4005 | 4044 | /* Release a memory-mapped page */ |
| @@ -4007,44 +4046,83 @@ | ||
| 4007 | 4046 | ApndFile *p = (ApndFile *)pFile; |
| 4008 | 4047 | pFile = ORIGFILE(pFile); |
| 4009 | 4048 | return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage); |
| 4010 | 4049 | } |
| 4011 | 4050 | |
| 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 | 4051 | /* |
| 4026 | 4052 | ** 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. | |
| 4029 | 4059 | */ |
| 4030 | 4060 | static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){ |
| 4031 | 4061 | int rc, i; |
| 4032 | 4062 | sqlite3_int64 iMark; |
| 4063 | + int msbs = 8 * (APND_MARK_FOS_SZ-1); | |
| 4033 | 4064 | unsigned char a[APND_MARK_SIZE]; |
| 4034 | 4065 | |
| 4035 | - if( sz<=APND_MARK_SIZE ) return -1; | |
| 4066 | + if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1; | |
| 4036 | 4067 | rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE); |
| 4037 | 4068 | if( rc ) return -1; |
| 4038 | 4069 | 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; | |
| 4042 | 4074 | } |
| 4075 | + if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1; | |
| 4076 | + if( iMark & 0x1ff ) return -1; | |
| 4043 | 4077 | return iMark; |
| 4044 | 4078 | } |
| 4045 | 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 | + | |
| 4046 | 4124 | /* |
| 4047 | 4125 | ** Open an apnd file handle. |
| 4048 | 4126 | */ |
| 4049 | 4127 | static int apndOpen( |
| 4050 | 4128 | sqlite3_vfs *pVfs, |
| @@ -4058,10 +4136,11 @@ | ||
| 4058 | 4136 | sqlite3_vfs *pSubVfs; |
| 4059 | 4137 | int rc; |
| 4060 | 4138 | sqlite3_int64 sz; |
| 4061 | 4139 | pSubVfs = ORIGVFS(pVfs); |
| 4062 | 4140 | if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ |
| 4141 | + /* The appendvfs is not to be used for transient or temporary databases. */ | |
| 4063 | 4142 | return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); |
| 4064 | 4143 | } |
| 4065 | 4144 | p = (ApndFile*)pFile; |
| 4066 | 4145 | memset(p, 0, sizeof(*p)); |
| 4067 | 4146 | pSubFile = ORIGFILE(pFile); |
| @@ -4075,31 +4154,46 @@ | ||
| 4075 | 4154 | } |
| 4076 | 4155 | if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){ |
| 4077 | 4156 | memmove(pFile, pSubFile, pSubVfs->szOsFile); |
| 4078 | 4157 | return SQLITE_OK; |
| 4079 | 4158 | } |
| 4080 | - p->iMark = 0; | |
| 4159 | + /* Record that append mark has not been written until seen otherwise. */ | |
| 4160 | + p->iMark = -1; | |
| 4081 | 4161 | 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; | |
| 4083 | 4165 | return SQLITE_OK; |
| 4084 | 4166 | } |
| 4085 | 4167 | if( (flags & SQLITE_OPEN_CREATE)==0 ){ |
| 4086 | 4168 | pSubFile->pMethods->xClose(pSubFile); |
| 4087 | 4169 | rc = SQLITE_CANTOPEN; |
| 4088 | 4170 | } |
| 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); | |
| 4090 | 4177 | apnd_open_done: |
| 4091 | 4178 | if( rc ) pFile->pMethods = 0; |
| 4092 | 4179 | return rc; |
| 4093 | 4180 | } |
| 4094 | 4181 | |
| 4095 | 4182 | /* |
| 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. | |
| 4097 | 4187 | */ |
| 4098 | 4188 | static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ |
| 4099 | 4189 | return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync); |
| 4100 | 4190 | } |
| 4191 | + | |
| 4192 | +/* | |
| 4193 | +** All other VFS methods are pass-thrus. | |
| 4194 | +*/ | |
| 4101 | 4195 | static int apndAccess( |
| 4102 | 4196 | sqlite3_vfs *pVfs, |
| 4103 | 4197 | const char *zPath, |
| 4104 | 4198 | int flags, |
| 4105 | 4199 | int *pResOut |
| @@ -5202,10 +5296,18 @@ | ||
| 5202 | 5296 | sqlite3_int64 m, e, a; |
| 5203 | 5297 | double r; |
| 5204 | 5298 | int isNeg = 0; |
| 5205 | 5299 | m = sqlite3_value_int64(argv[0]); |
| 5206 | 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 | + | |
| 5207 | 5309 | if( m<0 ){ |
| 5208 | 5310 | isNeg = 1; |
| 5209 | 5311 | m = -m; |
| 5210 | 5312 | if( m<0 ) return; |
| 5211 | 5313 | }else if( m==0 && e>-1000 && e<1000 ){ |
| @@ -19080,18 +19182,24 @@ | ||
| 19080 | 19182 | raw_printf(stderr, "Usage: .read FILE\n"); |
| 19081 | 19183 | rc = 1; |
| 19082 | 19184 | goto meta_command_exit; |
| 19083 | 19185 | } |
| 19084 | 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 | |
| 19085 | 19192 | p->in = popen(azArg[1]+1, "r"); |
| 19086 | 19193 | if( p->in==0 ){ |
| 19087 | 19194 | utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); |
| 19088 | 19195 | rc = 1; |
| 19089 | 19196 | }else{ |
| 19090 | 19197 | rc = process_input(p); |
| 19091 | 19198 | pclose(p->in); |
| 19092 | 19199 | } |
| 19200 | +#endif | |
| 19093 | 19201 | }else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){ |
| 19094 | 19202 | utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 19095 | 19203 | rc = 1; |
| 19096 | 19204 | }else{ |
| 19097 | 19205 | rc = process_input(p); |
| @@ -20865,11 +20973,12 @@ | ||
| 20865 | 20973 | } |
| 20866 | 20974 | return argv[i]; |
| 20867 | 20975 | } |
| 20868 | 20976 | |
| 20869 | 20977 | #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__))) | |
| 20871 | 20980 | # define SQLITE_SHELL_IS_UTF8 (0) |
| 20872 | 20981 | # else |
| 20873 | 20982 | # define SQLITE_SHELL_IS_UTF8 (1) |
| 20874 | 20983 | # endif |
| 20875 | 20984 | #endif |
| 20876 | 20985 |
| --- 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 @@ | ||
| 104 | 104 | ** |
| 105 | 105 | ** If the alternative skin name contains one or more '/' characters, then |
| 106 | 106 | ** it is assumed to be a directory on disk that holds override css.txt, |
| 107 | 107 | ** footer.txt, and header.txt. This mode can be used for interactive |
| 108 | 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. | |
| 109 | 142 | */ |
| 110 | -char *skin_use_alternative(const char *zName){ | |
| 143 | +char *skin_use_alternative(const char *zName, int rank){ | |
| 144 | + static int currentRank = 5; | |
| 111 | 145 | int i; |
| 112 | 146 | Blob err = BLOB_INITIALIZER; |
| 113 | - if( strchr(zName, '/')!=0 ){ | |
| 147 | + if(rank > currentRank) return 0; | |
| 148 | + if( zName && 1==rank && strchr(zName, '/')!=0 ){ | |
| 114 | 149 | zAltSkinDir = fossil_strdup(zName); |
| 115 | 150 | return 0; |
| 116 | 151 | } |
| 117 | - if( sqlite3_strglob("draft[1-9]", zName)==0 ){ | |
| 152 | + if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){ | |
| 118 | 153 | skin_use_draft(zName[5] - '0'); |
| 119 | 154 | return 0; |
| 155 | + } | |
| 156 | + if(!zName || !*zName){ | |
| 157 | + pAltSkin = 0; | |
| 158 | + zAltSkinDir = 0; | |
| 159 | + return 0; | |
| 120 | 160 | } |
| 121 | 161 | for(i=0; i<count(aBuiltinSkin); i++){ |
| 122 | 162 | if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){ |
| 123 | 163 | pAltSkin = &aBuiltinSkin[i]; |
| 124 | 164 | return 0; |
| @@ -137,11 +177,11 @@ | ||
| 137 | 177 | ** call fossil_fatal() if an unknown skin is specified. |
| 138 | 178 | */ |
| 139 | 179 | void skin_override(void){ |
| 140 | 180 | const char *zSkin = find_option("skin",0,1); |
| 141 | 181 | if( zSkin ){ |
| 142 | - char *zErr = skin_use_alternative(zSkin); | |
| 182 | + char *zErr = skin_use_alternative(zSkin, 1); | |
| 143 | 183 | if( zErr ) fossil_fatal("%s", zErr); |
| 144 | 184 | } |
| 145 | 185 | } |
| 146 | 186 | |
| 147 | 187 | /* |
| @@ -1154,24 +1194,32 @@ | ||
| 1154 | 1194 | } |
| 1155 | 1195 | fossil_free(zPattern); |
| 1156 | 1196 | } |
| 1157 | 1197 | login_check_credentials(); |
| 1158 | 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 | + } | |
| 1159 | 1207 | @ <p>The following skins are available for this repository:</p> |
| 1160 | 1208 | @ <ul> |
| 1161 | 1209 | if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){ |
| 1162 | 1210 | @ <li> Standard skin for this repository ← <i>Currently in use</i> |
| 1163 | 1211 | }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> | |
| 1165 | 1213 | } |
| 1166 | 1214 | for(i=0; i<count(aBuiltinSkin); i++){ |
| 1167 | 1215 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 1168 | 1216 | @ <li> %h(aBuiltinSkin[i].zDesc) ← <i>Currently in use</i> |
| 1169 | 1217 | }else{ |
| 1170 | - char *zUrl = href("%s/skn_%s/skins", zBase, aBuiltinSkin[i].zLabel); | |
| 1218 | + char *zUrl = href("%R/skins?skin=%T", aBuiltinSkin[i].zLabel); | |
| 1171 | 1219 | @ <li> %z(zUrl)%h(aBuiltinSkin[i].zDesc)</a> |
| 1172 | 1220 | } |
| 1173 | 1221 | } |
| 1174 | 1222 | @ </ul> |
| 1175 | 1223 | style_finish_page(); |
| 1176 | 1224 | fossil_free(zBase); |
| 1177 | 1225 | } |
| 1178 | 1226 |
| --- 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 ← <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) ← <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 ← <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) ← <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 |
+23
-4
| --- src/sqlcompattest.c | ||
| +++ src/sqlcompattest.c | ||
| @@ -13,15 +13,18 @@ | ||
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** http://www.hwaci.com/drh/ |
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | 17 | ** |
| 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. | |
| 19 | 20 | ** |
| 20 | 21 | ** This file contains a test program used by ../configure with the |
| 21 | 22 | ** the --disable-internal-sqlite option to determine whether or |
| 22 | 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. | |
| 23 | 26 | ** |
| 24 | 27 | ** It is preferred to statically link Fossil with the sqlite3.c source |
| 25 | 28 | ** file that is part of the source tree and not use any SQLite shared |
| 26 | 29 | ** library that is included with the system. But some packagers do not |
| 27 | 30 | ** like to do this. Hence, we provide the option to link Fossil against |
| @@ -38,23 +41,39 @@ | ||
| 38 | 41 | ** lacks some capability that Fossil uses. A message on stdout describes |
| 39 | 42 | ** the missing feature. |
| 40 | 43 | */ |
| 41 | 44 | #include "sqlite3.h" |
| 42 | 45 | #include <stdio.h> |
| 46 | +#include <string.h> | |
| 43 | 47 | |
| 44 | 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 | + | |
| 45 | 64 | int i; |
| 46 | 65 | static const char *zRequiredOpts[] = { |
| 47 | 66 | "ENABLE_FTS4", /* Required for repository search */ |
| 48 | 67 | "ENABLE_JSON1", /* Required for the check-in locking protocol */ |
| 49 | 68 | "ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */ |
| 50 | 69 | }; |
| 51 | 70 | |
| 52 | 71 | /* 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)); | |
| 56 | 75 | return 1; |
| 57 | 76 | } |
| 58 | 77 | |
| 59 | 78 | for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){ |
| 60 | 79 | if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){ |
| 61 | 80 |
| --- 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 @@ | ||
| 1186 | 1186 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1187 | 1187 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1188 | 1188 | */ |
| 1189 | 1189 | #define SQLITE_VERSION "3.35.0" |
| 1190 | 1190 | #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" | |
| 1192 | 1192 | |
| 1193 | 1193 | /* |
| 1194 | 1194 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1195 | 1195 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1196 | 1196 | ** |
| @@ -3193,11 +3193,17 @@ | ||
| 3193 | 3193 | ** The first argument is an integer which is 0 to disable views, |
| 3194 | 3194 | ** positive to enable views or negative to leave the setting unchanged. |
| 3195 | 3195 | ** The second parameter is a pointer to an integer into which |
| 3196 | 3196 | ** is written 0 or 1 to indicate whether views are disabled or enabled |
| 3197 | 3197 | ** 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> | |
| 3199 | 3205 | ** |
| 3200 | 3206 | ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] |
| 3201 | 3207 | ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> |
| 3202 | 3208 | ** <dd> ^This option is used to enable or disable the |
| 3203 | 3209 | ** [fts3_tokenizer()] function which is part of the |
| @@ -11617,22 +11623,27 @@ | ||
| 11617 | 11623 | ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator |
| 11618 | 11624 | ** created by [sqlite3changeset_start()]. In the latter case, the most recent |
| 11619 | 11625 | ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this |
| 11620 | 11626 | ** is not the case, this function returns [SQLITE_MISUSE]. |
| 11621 | 11627 | ** |
| 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 | |
| 11629 | 11642 | ** is an indirect change, or false (0) otherwise. See the documentation for |
| 11630 | 11643 | ** [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. | |
| 11634 | 11645 | ** |
| 11635 | 11646 | ** If no error occurs, SQLITE_OK is returned. If an error does occur, an |
| 11636 | 11647 | ** SQLite error code is returned. The values of the output variables may not |
| 11637 | 11648 | ** be trusted in this case. |
| 11638 | 11649 | */ |
| @@ -15368,11 +15379,11 @@ | ||
| 15368 | 15379 | |
| 15369 | 15380 | /* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */ |
| 15370 | 15381 | #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ |
| 15371 | 15382 | #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ |
| 15372 | 15383 | #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 */ | |
| 15374 | 15385 | |
| 15375 | 15386 | /* An instance of the BtreePayload object describes the content of a single |
| 15376 | 15387 | ** entry in either an index or table btree. |
| 15377 | 15388 | ** |
| 15378 | 15389 | ** Index btrees (used for indexes and also WITHOUT ROWID tables) contain |
| @@ -16942,10 +16953,15 @@ | ||
| 16942 | 16953 | #define SQLITE_TRACE_LEGACY 0 |
| 16943 | 16954 | #define SQLITE_TRACE_XPROFILE 0 |
| 16944 | 16955 | #endif /* SQLITE_OMIT_DEPRECATED */ |
| 16945 | 16956 | #define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */ |
| 16946 | 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) | |
| 16947 | 16963 | |
| 16948 | 16964 | /* |
| 16949 | 16965 | ** Each database connection is an instance of the following structure. |
| 16950 | 16966 | */ |
| 16951 | 16967 | struct sqlite3 { |
| @@ -18665,10 +18681,11 @@ | ||
| 18665 | 18681 | #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ |
| 18666 | 18682 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 18667 | 18683 | #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ |
| 18668 | 18684 | #define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ |
| 18669 | 18685 | #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ |
| 18686 | +#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ | |
| 18670 | 18687 | |
| 18671 | 18688 | /* |
| 18672 | 18689 | ** The results of a SELECT can be distributed in several ways, as defined |
| 18673 | 18690 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 18674 | 18691 | ** Type". |
| @@ -20255,10 +20272,11 @@ | ||
| 20255 | 20272 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int); |
| 20256 | 20273 | SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*); |
| 20257 | 20274 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*); |
| 20258 | 20275 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); |
| 20259 | 20276 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int); |
| 20277 | +SQLITE_PRIVATE const char *sqlite3SelectOpName(int); | |
| 20260 | 20278 | SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*); |
| 20261 | 20279 | |
| 20262 | 20280 | #ifdef SQLITE_DEBUG |
| 20263 | 20281 | SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*); |
| 20264 | 20282 | #endif |
| @@ -29329,11 +29347,11 @@ | ||
| 29329 | 29347 | setStrAccumError(p, SQLITE_TOOBIG); |
| 29330 | 29348 | return p->nAlloc - p->nChar - 1; |
| 29331 | 29349 | }else{ |
| 29332 | 29350 | char *zOld = isMalloced(p) ? p->zText : 0; |
| 29333 | 29351 | i64 szNew = p->nChar; |
| 29334 | - szNew += N + 1; | |
| 29352 | + szNew += (sqlite3_int64)N + 1; | |
| 29335 | 29353 | if( szNew+p->nChar<=p->mxAlloc ){ |
| 29336 | 29354 | /* Force exponential buffer size growth as long as it does not overflow, |
| 29337 | 29355 | ** to avoid having to call this routine too often */ |
| 29338 | 29356 | szNew += p->nChar; |
| 29339 | 29357 | } |
| @@ -50636,10 +50654,11 @@ | ||
| 50636 | 50654 | #endif |
| 50637 | 50655 | p->page.pBuf = pPg; |
| 50638 | 50656 | p->page.pExtra = &p[1]; |
| 50639 | 50657 | p->isBulkLocal = 0; |
| 50640 | 50658 | p->isAnchor = 0; |
| 50659 | + p->pLruPrev = 0; /* Initializing this saves a valgrind error */ | |
| 50641 | 50660 | } |
| 50642 | 50661 | (*pCache->pnPurgeable)++; |
| 50643 | 50662 | return p; |
| 50644 | 50663 | } |
| 50645 | 50664 | |
| @@ -52554,10 +52573,11 @@ | ||
| 52554 | 52573 | i64 iOffset; /* Starting offset in main journal */ |
| 52555 | 52574 | i64 iHdrOffset; /* See above */ |
| 52556 | 52575 | Bitvec *pInSavepoint; /* Set of pages in this savepoint */ |
| 52557 | 52576 | Pgno nOrig; /* Original number of pages in file */ |
| 52558 | 52577 | Pgno iSubRec; /* Index of first record in sub-journal */ |
| 52578 | + int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */ | |
| 52559 | 52579 | #ifndef SQLITE_OMIT_WAL |
| 52560 | 52580 | u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ |
| 52561 | 52581 | #endif |
| 52562 | 52582 | }; |
| 52563 | 52583 | |
| @@ -53189,10 +53209,13 @@ | ||
| 53189 | 53209 | Pgno pgno = pPg->pgno; |
| 53190 | 53210 | int i; |
| 53191 | 53211 | for(i=0; i<pPager->nSavepoint; i++){ |
| 53192 | 53212 | p = &pPager->aSavepoint[i]; |
| 53193 | 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 | + } | |
| 53194 | 53217 | return 1; |
| 53195 | 53218 | } |
| 53196 | 53219 | } |
| 53197 | 53220 | return 0; |
| 53198 | 53221 | } |
| @@ -58967,10 +58990,11 @@ | ||
| 58967 | 58990 | }else{ |
| 58968 | 58991 | aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); |
| 58969 | 58992 | } |
| 58970 | 58993 | aNew[ii].iSubRec = pPager->nSubRec; |
| 58971 | 58994 | aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); |
| 58995 | + aNew[ii].bTruncateOnRelease = 1; | |
| 58972 | 58996 | if( !aNew[ii].pInSavepoint ){ |
| 58973 | 58997 | return SQLITE_NOMEM_BKPT; |
| 58974 | 58998 | } |
| 58975 | 58999 | if( pagerUseWal(pPager) ){ |
| 58976 | 59000 | sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); |
| @@ -59048,17 +59072,19 @@ | ||
| 59048 | 59072 | pPager->nSavepoint = nNew; |
| 59049 | 59073 | |
| 59050 | 59074 | /* If this is a release of the outermost savepoint, truncate |
| 59051 | 59075 | ** the sub-journal to zero bytes in size. */ |
| 59052 | 59076 | if( op==SAVEPOINT_RELEASE ){ |
| 59053 | - if( nNew==0 && isOpen(pPager->sjfd) ){ | |
| 59077 | + PagerSavepoint *pRel = &pPager->aSavepoint[nNew]; | |
| 59078 | + if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){ | |
| 59054 | 59079 | /* Only truncate if it is an in-memory sub-journal. */ |
| 59055 | 59080 | if( sqlite3JournalIsInMemory(pPager->sjfd) ){ |
| 59056 | - rc = sqlite3OsTruncate(pPager->sjfd, 0); | |
| 59081 | + i64 sz = (pPager->pageSize+4)*pRel->iSubRec; | |
| 59082 | + rc = sqlite3OsTruncate(pPager->sjfd, sz); | |
| 59057 | 59083 | assert( rc==SQLITE_OK ); |
| 59058 | 59084 | } |
| 59059 | - pPager->nSubRec = 0; | |
| 59085 | + pPager->nSubRec = pRel->iSubRec; | |
| 59060 | 59086 | } |
| 59061 | 59087 | } |
| 59062 | 59088 | /* Else this is a rollback operation, playback the specified savepoint. |
| 59063 | 59089 | ** If this is a temp-file, it is possible that the journal file has |
| 59064 | 59090 | ** not yet been opened. In this case there have been no changes to |
| @@ -72558,11 +72584,13 @@ | ||
| 72558 | 72584 | }else{ |
| 72559 | 72585 | pRight = findCell(pParent, i+nxDiv-pParent->nOverflow); |
| 72560 | 72586 | } |
| 72561 | 72587 | pgno = get4byte(pRight); |
| 72562 | 72588 | 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 | + } | |
| 72564 | 72592 | if( rc ){ |
| 72565 | 72593 | memset(apOld, 0, (i+1)*sizeof(MemPage*)); |
| 72566 | 72594 | goto balance_cleanup; |
| 72567 | 72595 | } |
| 72568 | 72596 | if( apOld[i]->nFree<0 ){ |
| @@ -72597,16 +72625,14 @@ | ||
| 72597 | 72625 | ** buffer. It will be copied out again as soon as the aSpace[] buffer |
| 72598 | 72626 | ** is allocated. */ |
| 72599 | 72627 | if( pBt->btsFlags & BTS_FAST_SECURE ){ |
| 72600 | 72628 | int iOff; |
| 72601 | 72629 | |
| 72630 | + /* If the following if() condition is not true, the db is corrupted. | |
| 72631 | + ** The call to dropCell() below will detect this. */ | |
| 72602 | 72632 | 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 ){ | |
| 72608 | 72634 | memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); |
| 72609 | 72635 | apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; |
| 72610 | 72636 | } |
| 72611 | 72637 | } |
| 72612 | 72638 | dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); |
| @@ -87386,11 +87412,11 @@ | ||
| 87386 | 87412 | case OP_ChngCntRow: { |
| 87387 | 87413 | assert( pOp->p2==1 ); |
| 87388 | 87414 | if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){ |
| 87389 | 87415 | goto abort_due_to_error; |
| 87390 | 87416 | } |
| 87391 | - /* Fall through to the next case, OP_String */ | |
| 87417 | + /* Fall through to the next case, OP_ResultRow */ | |
| 87392 | 87418 | /* no break */ deliberate_fall_through |
| 87393 | 87419 | } |
| 87394 | 87420 | |
| 87395 | 87421 | /* Opcode: ResultRow P1 P2 * * * |
| 87396 | 87422 | ** Synopsis: output=r[P1@P2] |
| @@ -97963,11 +97989,10 @@ | ||
| 97963 | 97989 | struct MemJournal { |
| 97964 | 97990 | const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */ |
| 97965 | 97991 | int nChunkSize; /* In-memory chunk-size */ |
| 97966 | 97992 | |
| 97967 | 97993 | int nSpill; /* Bytes of data before flushing */ |
| 97968 | - int nSize; /* Bytes of data currently in memory */ | |
| 97969 | 97994 | FileChunk *pFirst; /* Head of in-memory chunk-list */ |
| 97970 | 97995 | FilePoint endpoint; /* Pointer to the end of the file */ |
| 97971 | 97996 | FilePoint readpoint; /* Pointer to the end of the last xRead() */ |
| 97972 | 97997 | |
| 97973 | 97998 | int flags; /* xOpen flags */ |
| @@ -98024,18 +98049,17 @@ | ||
| 98024 | 98049 | } |
| 98025 | 98050 | |
| 98026 | 98051 | /* |
| 98027 | 98052 | ** Free the list of FileChunk structures headed at MemJournal.pFirst. |
| 98028 | 98053 | */ |
| 98029 | -static void memjrnlFreeChunks(MemJournal *p){ | |
| 98054 | +static void memjrnlFreeChunks(FileChunk *pFirst){ | |
| 98030 | 98055 | FileChunk *pIter; |
| 98031 | 98056 | FileChunk *pNext; |
| 98032 | - for(pIter=p->pFirst; pIter; pIter=pNext){ | |
| 98057 | + for(pIter=pFirst; pIter; pIter=pNext){ | |
| 98033 | 98058 | pNext = pIter->pNext; |
| 98034 | 98059 | sqlite3_free(pIter); |
| 98035 | 98060 | } |
| 98036 | - p->pFirst = 0; | |
| 98037 | 98061 | } |
| 98038 | 98062 | |
| 98039 | 98063 | /* |
| 98040 | 98064 | ** Flush the contents of memory to a real file on disk. |
| 98041 | 98065 | */ |
| @@ -98058,11 +98082,11 @@ | ||
| 98058 | 98082 | if( rc ) break; |
| 98059 | 98083 | iOff += nChunk; |
| 98060 | 98084 | } |
| 98061 | 98085 | if( rc==SQLITE_OK ){ |
| 98062 | 98086 | /* No error has occurred. Free the in-memory buffers. */ |
| 98063 | - memjrnlFreeChunks(©); | |
| 98087 | + memjrnlFreeChunks(copy.pFirst); | |
| 98064 | 98088 | } |
| 98065 | 98089 | } |
| 98066 | 98090 | if( rc!=SQLITE_OK ){ |
| 98067 | 98091 | /* If an error occurred while creating or writing to the file, restore |
| 98068 | 98092 | ** the original before returning. This way, SQLite uses the in-memory |
| @@ -98141,43 +98165,50 @@ | ||
| 98141 | 98165 | memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace); |
| 98142 | 98166 | zWrite += iSpace; |
| 98143 | 98167 | nWrite -= iSpace; |
| 98144 | 98168 | p->endpoint.iOffset += iSpace; |
| 98145 | 98169 | } |
| 98146 | - p->nSize = iAmt + iOfst; | |
| 98147 | 98170 | } |
| 98148 | 98171 | } |
| 98149 | 98172 | |
| 98150 | 98173 | return SQLITE_OK; |
| 98151 | 98174 | } |
| 98152 | 98175 | |
| 98153 | 98176 | /* |
| 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. | |
| 98159 | 98178 | */ |
| 98160 | 98179 | static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ |
| 98161 | 98180 | 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; | |
| 98170 | 98201 | return SQLITE_OK; |
| 98171 | 98202 | } |
| 98172 | 98203 | |
| 98173 | 98204 | /* |
| 98174 | 98205 | ** Close the file. |
| 98175 | 98206 | */ |
| 98176 | 98207 | static int memjrnlClose(sqlite3_file *pJfd){ |
| 98177 | 98208 | MemJournal *p = (MemJournal *)pJfd; |
| 98178 | - memjrnlFreeChunks(p); | |
| 98209 | + memjrnlFreeChunks(p->pFirst); | |
| 98179 | 98210 | return SQLITE_OK; |
| 98180 | 98211 | } |
| 98181 | 98212 | |
| 98182 | 98213 | /* |
| 98183 | 98214 | ** Sync the file. |
| @@ -98992,10 +99023,11 @@ | ||
| 98992 | 99023 | /* IMP: R-51414-32910 */ |
| 98993 | 99024 | iCol = -1; |
| 98994 | 99025 | } |
| 98995 | 99026 | if( iCol<pTab->nCol ){ |
| 98996 | 99027 | cnt++; |
| 99028 | + pMatch = 0; | |
| 98997 | 99029 | #ifndef SQLITE_OMIT_UPSERT |
| 98998 | 99030 | if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){ |
| 98999 | 99031 | testcase( iCol==(-1) ); |
| 99000 | 99032 | if( IN_RENAME_OBJECT ){ |
| 99001 | 99033 | pExpr->iColumn = iCol; |
| @@ -99010,12 +99042,12 @@ | ||
| 99010 | 99042 | #endif /* SQLITE_OMIT_UPSERT */ |
| 99011 | 99043 | { |
| 99012 | 99044 | pExpr->y.pTab = pTab; |
| 99013 | 99045 | if( pParse->bReturning ){ |
| 99014 | 99046 | 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; | |
| 99017 | 99049 | }else{ |
| 99018 | 99050 | pExpr->iColumn = (i16)iCol; |
| 99019 | 99051 | eNewExprOp = TK_TRIGGER; |
| 99020 | 99052 | #ifndef SQLITE_OMIT_TRIGGER |
| 99021 | 99053 | if( iCol<0 ){ |
| @@ -99211,15 +99243,17 @@ | ||
| 99211 | 99243 | pExpr->op = eNewExprOp; |
| 99212 | 99244 | ExprSetProperty(pExpr, EP_Leaf); |
| 99213 | 99245 | lookupname_end: |
| 99214 | 99246 | if( cnt==1 ){ |
| 99215 | 99247 | assert( pNC!=0 ); |
| 99248 | +#ifndef SQLITE_OMIT_AUTHORIZATION | |
| 99216 | 99249 | if( pParse->db->xAuth |
| 99217 | 99250 | && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER) |
| 99218 | 99251 | ){ |
| 99219 | 99252 | sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); |
| 99220 | 99253 | } |
| 99254 | +#endif | |
| 99221 | 99255 | /* Increment the nRef value on all name contexts from TopNC up to |
| 99222 | 99256 | ** the point where the name matched. */ |
| 99223 | 99257 | for(;;){ |
| 99224 | 99258 | assert( pTopNC!=0 ); |
| 99225 | 99259 | pTopNC->nRef++; |
| @@ -99359,10 +99393,51 @@ | ||
| 99359 | 99393 | pExpr->iTable = pItem->iCursor; |
| 99360 | 99394 | pExpr->iColumn--; |
| 99361 | 99395 | pExpr->affExpr = SQLITE_AFF_INTEGER; |
| 99362 | 99396 | break; |
| 99363 | 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 | + } | |
| 99364 | 99439 | |
| 99365 | 99440 | /* A column name: ID |
| 99366 | 99441 | ** Or table name and column name: ID.ID |
| 99367 | 99442 | ** Or a database, table and column: ID.ID.ID |
| 99368 | 99443 | ** |
| @@ -99587,10 +99662,11 @@ | ||
| 99587 | 99662 | if( pWin ){ |
| 99588 | 99663 | Select *pSel = pNC->pWinSelect; |
| 99589 | 99664 | assert( pWin==pExpr->y.pWin ); |
| 99590 | 99665 | if( IN_RENAME_OBJECT==0 ){ |
| 99591 | 99666 | sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); |
| 99667 | + if( pParse->db->mallocFailed ) break; | |
| 99592 | 99668 | } |
| 99593 | 99669 | sqlite3WalkExprList(pWalker, pWin->pPartition); |
| 99594 | 99670 | sqlite3WalkExprList(pWalker, pWin->pOrderBy); |
| 99595 | 99671 | sqlite3WalkExpr(pWalker, pWin->pFilter); |
| 99596 | 99672 | sqlite3WindowLink(pSel, pWin); |
| @@ -99661,11 +99737,11 @@ | ||
| 99661 | 99737 | case TK_ISNOT: { |
| 99662 | 99738 | Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight); |
| 99663 | 99739 | assert( !ExprHasProperty(pExpr, EP_Reduced) ); |
| 99664 | 99740 | /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", |
| 99665 | 99741 | ** 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) ){ | |
| 99667 | 99743 | int rc = resolveExprStep(pWalker, pRight); |
| 99668 | 99744 | if( rc==WRC_Abort ) return WRC_Abort; |
| 99669 | 99745 | if( pRight->op==TK_TRUEFALSE ){ |
| 99670 | 99746 | pExpr->op2 = pExpr->op; |
| 99671 | 99747 | pExpr->op = TK_TRUTH; |
| @@ -100164,29 +100240,28 @@ | ||
| 100164 | 100240 | /* Recursively resolve names in all subqueries |
| 100165 | 100241 | */ |
| 100166 | 100242 | for(i=0; i<p->pSrc->nSrc; i++){ |
| 100167 | 100243 | SrcItem *pItem = &p->pSrc->a[i]; |
| 100168 | 100244 | 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; | |
| 100171 | 100246 | const char *zSavedContext = pParse->zAuthContext; |
| 100172 | 100247 | |
| 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 | 100248 | if( pItem->zName ) pParse->zAuthContext = pItem->zName; |
| 100181 | 100249 | sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); |
| 100182 | 100250 | pParse->zAuthContext = zSavedContext; |
| 100183 | 100251 | if( pParse->nErr || db->mallocFailed ) return WRC_Abort; |
| 100184 | 100252 | |
| 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 | + } | |
| 100188 | 100263 | } |
| 100189 | 100264 | } |
| 100190 | 100265 | |
| 100191 | 100266 | /* Set up the local name-context to pass to sqlite3ResolveExprNames() to |
| 100192 | 100267 | ** resolve the result-set expression list. |
| @@ -103131,11 +103206,11 @@ | ||
| 103131 | 103206 | assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ |
| 103132 | 103207 | pTab = p->pSrc->a[0].pTab; |
| 103133 | 103208 | |
| 103134 | 103209 | /* Code an OP_Transaction and OP_TableLock for <table>. */ |
| 103135 | 103210 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 103136 | - assert( iDb>=0 && iDb<SQLITE_MAX_ATTACHED ); | |
| 103211 | + assert( iDb>=0 && iDb<SQLITE_MAX_DB ); | |
| 103137 | 103212 | sqlite3CodeVerifySchema(pParse, iDb); |
| 103138 | 103213 | sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); |
| 103139 | 103214 | |
| 103140 | 103215 | assert(v); /* sqlite3GetVdbe() has always been previously called */ |
| 103141 | 103216 | if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){ |
| @@ -107891,13 +107966,12 @@ | ||
| 107891 | 107966 | } |
| 107892 | 107967 | if( rc==SQLITE_OK ){ |
| 107893 | 107968 | rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); |
| 107894 | 107969 | } |
| 107895 | 107970 | assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); |
| 107896 | - if( pStep->pUpsert ){ | |
| 107971 | + if( pStep->pUpsert && rc==SQLITE_OK ){ | |
| 107897 | 107972 | Upsert *pUpsert = pStep->pUpsert; |
| 107898 | - assert( rc==SQLITE_OK ); | |
| 107899 | 107973 | pUpsert->pUpsertSrc = pSrc; |
| 107900 | 107974 | sNC.uNC.pUpsert = pUpsert; |
| 107901 | 107975 | sNC.ncFlags = NC_UUpsert; |
| 107902 | 107976 | rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); |
| 107903 | 107977 | if( rc==SQLITE_OK ){ |
| @@ -108453,14 +108527,15 @@ | ||
| 108453 | 108527 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108454 | 108528 | sqlite3_xauth xAuth = db->xAuth; |
| 108455 | 108529 | db->xAuth = 0; |
| 108456 | 108530 | #endif |
| 108457 | 108531 | |
| 108532 | + UNUSED_PARAMETER(NotUsed); | |
| 108458 | 108533 | rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); |
| 108459 | 108534 | if( rc!=SQLITE_OK ) goto drop_column_done; |
| 108460 | 108535 | pTab = sParse.pNewTable; |
| 108461 | - if( pTab->nCol==1 || iCol>=pTab->nCol ){ | |
| 108536 | + if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ | |
| 108462 | 108537 | /* This can happen if the sqlite_schema table is corrupt */ |
| 108463 | 108538 | rc = SQLITE_CORRUPT_BKPT; |
| 108464 | 108539 | goto drop_column_done; |
| 108465 | 108540 | } |
| 108466 | 108541 | |
| @@ -112764,10 +112839,11 @@ | ||
| 112764 | 112839 | pParse->u1.pReturning = pRet; |
| 112765 | 112840 | pRet->pParse = pParse; |
| 112766 | 112841 | pRet->pReturnEL = pList; |
| 112767 | 112842 | sqlite3ParserAddCleanup(pParse, |
| 112768 | 112843 | (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet); |
| 112844 | + testcase( pParse->earlyCleanup ); | |
| 112769 | 112845 | if( db->mallocFailed ) return; |
| 112770 | 112846 | pRet->retTrig.zName = RETURNING_TRIGGER_NAME; |
| 112771 | 112847 | pRet->retTrig.op = TK_RETURNING; |
| 112772 | 112848 | pRet->retTrig.tr_tm = TRIGGER_AFTER; |
| 112773 | 112849 | pRet->retTrig.bReturning = 1; |
| @@ -114298,10 +114374,11 @@ | ||
| 114298 | 114374 | ** the column names from the SELECT statement that defines the view. |
| 114299 | 114375 | */ |
| 114300 | 114376 | assert( pTable->aCol==0 ); |
| 114301 | 114377 | pTable->nCol = pSelTab->nCol; |
| 114302 | 114378 | pTable->aCol = pSelTab->aCol; |
| 114379 | + pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT); | |
| 114303 | 114380 | pSelTab->nCol = 0; |
| 114304 | 114381 | pSelTab->aCol = 0; |
| 114305 | 114382 | assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); |
| 114306 | 114383 | } |
| 114307 | 114384 | pTable->nNVCol = pTable->nCol; |
| @@ -116301,11 +116378,11 @@ | ||
| 116301 | 116378 | ** later, by sqlite3FinishCoding(). |
| 116302 | 116379 | */ |
| 116303 | 116380 | static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){ |
| 116304 | 116381 | assert( iDb>=0 && iDb<pToplevel->db->nDb ); |
| 116305 | 116382 | assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 ); |
| 116306 | - assert( iDb<SQLITE_MAX_ATTACHED+2 ); | |
| 116383 | + assert( iDb<SQLITE_MAX_DB ); | |
| 116307 | 116384 | assert( sqlite3SchemaMutexHeld(pToplevel->db, iDb, 0) ); |
| 116308 | 116385 | if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){ |
| 116309 | 116386 | DbMaskSet(pToplevel->cookieMask, iDb); |
| 116310 | 116387 | if( !OMIT_TEMPDB && iDb==1 ){ |
| 116311 | 116388 | sqlite3OpenTempDatabase(pToplevel); |
| @@ -117505,14 +117582,16 @@ | ||
| 117505 | 117582 | } |
| 117506 | 117583 | |
| 117507 | 117584 | /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree |
| 117508 | 117585 | ** and the SELECT subtree. */ |
| 117509 | 117586 | pSrc->a[0].pTab = 0; |
| 117510 | - pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0); | |
| 117587 | + pSelectSrc = sqlite3SrcListDup(db, pSrc, 0); | |
| 117511 | 117588 | pSrc->a[0].pTab = pTab; |
| 117512 | 117589 | if( pSrc->a[0].fg.isIndexedBy ){ |
| 117513 | 117590 | pSrc->a[0].u2.pIBIndex = 0; |
| 117591 | + pSrc->a[0].fg.isIndexedBy = 0; | |
| 117592 | + sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy); | |
| 117514 | 117593 | }else if( pSrc->a[0].fg.isCte ){ |
| 117515 | 117594 | pSrc->a[0].u2.pCteUse->nUse++; |
| 117516 | 117595 | } |
| 117517 | 117596 | |
| 117518 | 117597 | /* generate the SELECT expression tree. */ |
| @@ -129355,11 +129434,11 @@ | ||
| 129355 | 129434 | ** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate |
| 129356 | 129435 | ** |
| 129357 | 129436 | ** Checkpoint the database. |
| 129358 | 129437 | */ |
| 129359 | 129438 | case PragTyp_WAL_CHECKPOINT: { |
| 129360 | - int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); | |
| 129439 | + int iBt = (pId2->z?iDb:SQLITE_MAX_DB); | |
| 129361 | 129440 | int eMode = SQLITE_CHECKPOINT_PASSIVE; |
| 129362 | 129441 | if( zRight ){ |
| 129363 | 129442 | if( sqlite3StrICmp(zRight, "full")==0 ){ |
| 129364 | 129443 | eMode = SQLITE_CHECKPOINT_FULL; |
| 129365 | 129444 | }else if( sqlite3StrICmp(zRight, "restart")==0 ){ |
| @@ -130571,17 +130650,25 @@ | ||
| 130571 | 130650 | ** for common cleanups that happen on most calls. But for less |
| 130572 | 130651 | ** common cleanups, we save a single NULL-pointer comparison in |
| 130573 | 130652 | ** sqlite3ParserReset(), which reduces the total CPU cycle count. |
| 130574 | 130653 | ** |
| 130575 | 130654 | ** 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 | |
| 130577 | 130656 | ** pParse->earlyCleanup flag is set in that case. Calling code show verify |
| 130578 | 130657 | ** that test cases exist for which this happens, to guard against possible |
| 130579 | 130658 | ** use-after-free errors following an OOM. The preferred way to do this is |
| 130580 | 130659 | ** to immediately follow the call to this routine with: |
| 130581 | 130660 | ** |
| 130582 | 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); | |
| 130583 | 130670 | */ |
| 130584 | 130671 | SQLITE_PRIVATE void *sqlite3ParserAddCleanup( |
| 130585 | 130672 | Parse *pParse, /* Destroy when this Parser finishes */ |
| 130586 | 130673 | void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ |
| 130587 | 130674 | void *pPtr /* Pointer to object to be cleaned up */ |
| @@ -131075,16 +131162,20 @@ | ||
| 131075 | 131162 | sqlite3ExprDelete(db, p->pWhere); |
| 131076 | 131163 | sqlite3ExprListDelete(db, p->pGroupBy); |
| 131077 | 131164 | sqlite3ExprDelete(db, p->pHaving); |
| 131078 | 131165 | sqlite3ExprListDelete(db, p->pOrderBy); |
| 131079 | 131166 | sqlite3ExprDelete(db, p->pLimit); |
| 131167 | + if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); | |
| 131080 | 131168 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 131081 | 131169 | if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){ |
| 131082 | 131170 | sqlite3WindowListDelete(db, p->pWinDefn); |
| 131083 | 131171 | } |
| 131172 | + while( p->pWin ){ | |
| 131173 | + assert( p->pWin->ppThis==&p->pWin ); | |
| 131174 | + sqlite3WindowUnlinkFromSelect(p->pWin); | |
| 131175 | + } | |
| 131084 | 131176 | #endif |
| 131085 | - if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); | |
| 131086 | 131177 | if( bFree ) sqlite3DbFreeNN(db, p); |
| 131087 | 131178 | p = pPrior; |
| 131088 | 131179 | bFree = 1; |
| 131089 | 131180 | } |
| 131090 | 131181 | } |
| @@ -131396,10 +131487,13 @@ | ||
| 131396 | 131487 | static void unsetJoinExpr(Expr *p, int iTable){ |
| 131397 | 131488 | while( p ){ |
| 131398 | 131489 | if( ExprHasProperty(p, EP_FromJoin) |
| 131399 | 131490 | && (iTable<0 || p->iRightJoinTable==iTable) ){ |
| 131400 | 131491 | ExprClearProperty(p, EP_FromJoin); |
| 131492 | + } | |
| 131493 | + if( p->op==TK_COLUMN && p->iTable==iTable ){ | |
| 131494 | + ExprClearProperty(p, EP_CanBeNull); | |
| 131401 | 131495 | } |
| 131402 | 131496 | if( p->op==TK_FUNCTION && p->x.pList ){ |
| 131403 | 131497 | int i; |
| 131404 | 131498 | for(i=0; i<p->x.pList->nExpr; i++){ |
| 131405 | 131499 | unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); |
| @@ -132373,11 +132467,11 @@ | ||
| 132373 | 132467 | } |
| 132374 | 132468 | |
| 132375 | 132469 | /* |
| 132376 | 132470 | ** Name of the connection operator, used for error messages. |
| 132377 | 132471 | */ |
| 132378 | -static const char *selectOpName(int id){ | |
| 132472 | +SQLITE_PRIVATE const char *sqlite3SelectOpName(int id){ | |
| 132379 | 132473 | char *z; |
| 132380 | 132474 | switch( id ){ |
| 132381 | 132475 | case TK_ALL: z = "UNION ALL"; break; |
| 132382 | 132476 | case TK_INTERSECT: z = "INTERSECT"; break; |
| 132383 | 132477 | case TK_EXCEPT: z = "EXCEPT"; break; |
| @@ -133592,16 +133686,12 @@ | ||
| 133592 | 133686 | assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION ); |
| 133593 | 133687 | assert( p->selFlags & SF_Compound ); |
| 133594 | 133688 | db = pParse->db; |
| 133595 | 133689 | pPrior = p->pPrior; |
| 133596 | 133690 | 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 ); | |
| 133603 | 133693 | |
| 133604 | 133694 | v = sqlite3GetVdbe(pParse); |
| 133605 | 133695 | assert( v!=0 ); /* The VDBE already created by calling function */ |
| 133606 | 133696 | |
| 133607 | 133697 | /* Create the destination temporary table if necessary |
| @@ -133654,11 +133744,11 @@ | ||
| 133654 | 133744 | assert( !pPrior->pLimit ); |
| 133655 | 133745 | pPrior->iLimit = p->iLimit; |
| 133656 | 133746 | pPrior->iOffset = p->iOffset; |
| 133657 | 133747 | pPrior->pLimit = p->pLimit; |
| 133658 | 133748 | rc = sqlite3Select(pParse, pPrior, &dest); |
| 133659 | - p->pLimit = 0; | |
| 133749 | + pPrior->pLimit = 0; | |
| 133660 | 133750 | if( rc ){ |
| 133661 | 133751 | goto multi_select_end; |
| 133662 | 133752 | } |
| 133663 | 133753 | p->pPrior = 0; |
| 133664 | 133754 | p->iLimit = pPrior->iLimit; |
| @@ -133675,12 +133765,12 @@ | ||
| 133675 | 133765 | rc = sqlite3Select(pParse, p, &dest); |
| 133676 | 133766 | testcase( rc!=SQLITE_OK ); |
| 133677 | 133767 | pDelete = p->pPrior; |
| 133678 | 133768 | p->pPrior = pPrior; |
| 133679 | 133769 | 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) | |
| 133682 | 133772 | && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) |
| 133683 | 133773 | ){ |
| 133684 | 133774 | p->nSelectRow = sqlite3LogEst((u64)nLimit); |
| 133685 | 133775 | } |
| 133686 | 133776 | if( addr ){ |
| @@ -133740,11 +133830,11 @@ | ||
| 133740 | 133830 | p->pPrior = 0; |
| 133741 | 133831 | pLimit = p->pLimit; |
| 133742 | 133832 | p->pLimit = 0; |
| 133743 | 133833 | uniondest.eDest = op; |
| 133744 | 133834 | ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", |
| 133745 | - selectOpName(p->op))); | |
| 133835 | + sqlite3SelectOpName(p->op))); | |
| 133746 | 133836 | rc = sqlite3Select(pParse, p, &uniondest); |
| 133747 | 133837 | testcase( rc!=SQLITE_OK ); |
| 133748 | 133838 | assert( p->pOrderBy==0 ); |
| 133749 | 133839 | pDelete = p->pPrior; |
| 133750 | 133840 | p->pPrior = pPrior; |
| @@ -133816,11 +133906,11 @@ | ||
| 133816 | 133906 | p->pPrior = 0; |
| 133817 | 133907 | pLimit = p->pLimit; |
| 133818 | 133908 | p->pLimit = 0; |
| 133819 | 133909 | intersectdest.iSDParm = tab2; |
| 133820 | 133910 | ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", |
| 133821 | - selectOpName(p->op))); | |
| 133911 | + sqlite3SelectOpName(p->op))); | |
| 133822 | 133912 | rc = sqlite3Select(pParse, p, &intersectdest); |
| 133823 | 133913 | testcase( rc!=SQLITE_OK ); |
| 133824 | 133914 | pDelete = p->pPrior; |
| 133825 | 133915 | p->pPrior = pPrior; |
| 133826 | 133916 | if( p->nSelectRow>pPrior->nSelectRow ){ |
| @@ -133925,11 +134015,12 @@ | ||
| 133925 | 134015 | SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){ |
| 133926 | 134016 | if( p->selFlags & SF_Values ){ |
| 133927 | 134017 | sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); |
| 133928 | 134018 | }else{ |
| 133929 | 134019 | 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)); | |
| 133931 | 134022 | } |
| 133932 | 134023 | } |
| 133933 | 134024 | |
| 133934 | 134025 | /* |
| 133935 | 134026 | ** Code an output subroutine for a coroutine implementation of a |
| @@ -134022,14 +134113,12 @@ | ||
| 134022 | 134113 | ** store the results in the appropriate memory cell and break out |
| 134023 | 134114 | ** of the scan loop. Note that the select might return multiple columns |
| 134024 | 134115 | ** if it is the RHS of a row-value IN operator. |
| 134025 | 134116 | */ |
| 134026 | 134117 | 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); | |
| 134031 | 134120 | /* The LIMIT clause will jump out of the loop for us */ |
| 134032 | 134121 | break; |
| 134033 | 134122 | } |
| 134034 | 134123 | #endif /* #ifndef SQLITE_OMIT_SUBQUERY */ |
| 134035 | 134124 | |
| @@ -134317,11 +134406,11 @@ | ||
| 134317 | 134406 | regOutA = ++pParse->nMem; |
| 134318 | 134407 | regOutB = ++pParse->nMem; |
| 134319 | 134408 | sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); |
| 134320 | 134409 | sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); |
| 134321 | 134410 | |
| 134322 | - ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op))); | |
| 134411 | + ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlite3SelectOpName(p->op))); | |
| 134323 | 134412 | |
| 134324 | 134413 | /* Generate a coroutine to evaluate the SELECT statement to the |
| 134325 | 134414 | ** left of the compound operator - the "A" select. |
| 134326 | 134415 | */ |
| 134327 | 134416 | addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; |
| @@ -135479,10 +135568,39 @@ | ||
| 135479 | 135568 | } |
| 135480 | 135569 | }while( x.nChng ); |
| 135481 | 135570 | return nChng; |
| 135482 | 135571 | } |
| 135483 | 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 | + | |
| 135484 | 135602 | #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) |
| 135485 | 135603 | /* |
| 135486 | 135604 | ** Make copies of relevant WHERE clause terms of the outer query into |
| 135487 | 135605 | ** the WHERE clause of subquery. Example: |
| 135488 | 135606 | ** |
| @@ -135526,13 +135644,24 @@ | ||
| 135526 | 135644 | ** |
| 135527 | 135645 | ** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9). |
| 135528 | 135646 | ** But if the (b2=2) term were to be pushed down into the bb subquery, |
| 135529 | 135647 | ** then the (1,1,NULL) row would be suppressed. |
| 135530 | 135648 | ** |
| 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. | |
| 135534 | 135663 | ** |
| 135535 | 135664 | ** (7) The inner query is a Common Table Expression (CTE) that should |
| 135536 | 135665 | ** be materialized. (This restriction is implemented in the calling |
| 135537 | 135666 | ** routine.) |
| 135538 | 135667 | ** |
| @@ -135546,17 +135675,21 @@ | ||
| 135546 | 135675 | int iCursor, /* Cursor number of the subquery */ |
| 135547 | 135676 | int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ |
| 135548 | 135677 | ){ |
| 135549 | 135678 | Expr *pNew; |
| 135550 | 135679 | int nChng = 0; |
| 135551 | - Select *pSel; | |
| 135552 | 135680 | 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; | |
| 135554 | 135682 | |
| 135555 | 135683 | #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; | |
| 135558 | 135691 | } |
| 135559 | 135692 | #endif |
| 135560 | 135693 | |
| 135561 | 135694 | #ifdef SQLITE_DEBUG |
| 135562 | 135695 | /* Only the first term of a compound can have a WITH clause. But make |
| @@ -135599,10 +135732,18 @@ | ||
| 135599 | 135732 | x.iTable = iCursor; |
| 135600 | 135733 | x.iNewTable = iCursor; |
| 135601 | 135734 | x.isLeftJoin = 0; |
| 135602 | 135735 | x.pEList = pSubq->pEList; |
| 135603 | 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 | |
| 135604 | 135745 | if( pSubq->selFlags & SF_Aggregate ){ |
| 135605 | 135746 | pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew); |
| 135606 | 135747 | }else{ |
| 135607 | 135748 | pSubq->pWhere = sqlite3ExprAnd(pParse, pSubq->pWhere, pNew); |
| 135608 | 135749 | } |
| @@ -136190,11 +136331,14 @@ | ||
| 136190 | 136331 | if( IsVirtual(pTab) || pTab->pSelect ){ |
| 136191 | 136332 | i16 nCol; |
| 136192 | 136333 | u8 eCodeOrig = pWalker->eCode; |
| 136193 | 136334 | if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; |
| 136194 | 136335 | 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 | + ){ | |
| 136196 | 136340 | sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", |
| 136197 | 136341 | pTab->zName); |
| 136198 | 136342 | } |
| 136199 | 136343 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 136200 | 136344 | if( IsVirtual(pTab) |
| @@ -136969,12 +137113,23 @@ | ||
| 136969 | 137113 | if( IgnorableDistinct(pDest) ){ |
| 136970 | 137114 | assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || |
| 136971 | 137115 | pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || |
| 136972 | 137116 | pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); |
| 136973 | 137117 | /* 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 | + } | |
| 136976 | 137131 | p->selFlags &= ~SF_Distinct; |
| 136977 | 137132 | p->selFlags |= SF_NoopOrderBy; |
| 136978 | 137133 | } |
| 136979 | 137134 | sqlite3SelectPrep(pParse, p, 0); |
| 136980 | 137135 | if( pParse->nErr || db->mallocFailed ){ |
| @@ -137589,10 +137744,11 @@ | ||
| 137589 | 137744 | */ |
| 137590 | 137745 | pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); |
| 137591 | 137746 | if( pAggInfo ){ |
| 137592 | 137747 | sqlite3ParserAddCleanup(pParse, |
| 137593 | 137748 | (void(*)(sqlite3*,void*))agginfoFree, pAggInfo); |
| 137749 | + testcase( pParse->earlyCleanup ); | |
| 137594 | 137750 | } |
| 137595 | 137751 | if( db->mallocFailed ){ |
| 137596 | 137752 | goto select_end; |
| 137597 | 137753 | } |
| 137598 | 137754 | pAggInfo->selId = p->selId; |
| @@ -147345,11 +147501,14 @@ | ||
| 147345 | 147501 | sqlite3 *db = pParse->db; |
| 147346 | 147502 | |
| 147347 | 147503 | assert( pExpr->op==TK_EXISTS ); |
| 147348 | 147504 | assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) ); |
| 147349 | 147505 | |
| 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 | |
| 147351 | 147510 | if( pSel->pPrior ) return; |
| 147352 | 147511 | if( pSel->pWhere==0 ) return; |
| 147353 | 147512 | if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return; |
| 147354 | 147513 | |
| 147355 | 147514 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| @@ -147542,10 +147701,16 @@ | ||
| 147542 | 147701 | pNew->u.x.leftColumn = aiCurCol[1]; |
| 147543 | 147702 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 147544 | 147703 | pNew->prereqRight = prereqLeft | extraRight; |
| 147545 | 147704 | pNew->prereqAll = prereqAll; |
| 147546 | 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; | |
| 147547 | 147712 | } |
| 147548 | 147713 | } |
| 147549 | 147714 | |
| 147550 | 147715 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 147551 | 147716 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -153019,11 +153184,11 @@ | ||
| 153019 | 153184 | } |
| 153020 | 153185 | } |
| 153021 | 153186 | if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ |
| 153022 | 153187 | pWInfo->revMask = ALLBITS; |
| 153023 | 153188 | } |
| 153024 | - if( pParse->nErr || NEVER(db->mallocFailed) ){ | |
| 153189 | + if( pParse->nErr || db->mallocFailed ){ | |
| 153025 | 153190 | goto whereBeginError; |
| 153026 | 153191 | } |
| 153027 | 153192 | #ifdef WHERETRACE_ENABLED |
| 153028 | 153193 | if( sqlite3WhereTrace ){ |
| 153029 | 153194 | sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); |
| @@ -154961,19 +155126,23 @@ | ||
| 154961 | 155126 | ** to be processed as part of SELECT statement pSel). The window is linked |
| 154962 | 155127 | ** in if either (a) there are no other windows already linked to this |
| 154963 | 155128 | ** SELECT, or (b) the windows already linked use a compatible window frame. |
| 154964 | 155129 | */ |
| 154965 | 155130 | 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 | + } | |
| 154975 | 155144 | } |
| 154976 | 155145 | } |
| 154977 | 155146 | |
| 154978 | 155147 | /* |
| 154979 | 155148 | ** Return 0 if the two window objects are identical, 1 if they are |
| @@ -155718,10 +155887,11 @@ | ||
| 155718 | 155887 | int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */ |
| 155719 | 155888 | int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */ |
| 155720 | 155889 | int regString = ++pParse->nMem; /* Reg. for constant value '' */ |
| 155721 | 155890 | int arith = OP_Add; /* OP_Add or OP_Subtract */ |
| 155722 | 155891 | int addrGe; /* Jump destination */ |
| 155892 | + CollSeq *pColl; | |
| 155723 | 155893 | |
| 155724 | 155894 | assert( op==OP_Ge || op==OP_Gt || op==OP_Le ); |
| 155725 | 155895 | assert( pOrderBy && pOrderBy->nExpr==1 ); |
| 155726 | 155896 | if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){ |
| 155727 | 155897 | switch( op ){ |
| @@ -155808,10 +155978,12 @@ | ||
| 155808 | 155978 | |
| 155809 | 155979 | /* Compare registers reg2 and reg1, taking the jump if required. Note that |
| 155810 | 155980 | ** control skips over this test if the BIGNULL flag is set and either |
| 155811 | 155981 | ** reg1 or reg2 contain a NULL value. */ |
| 155812 | 155982 | sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); |
| 155983 | + pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr); | |
| 155984 | + sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ); | |
| 155813 | 155985 | sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); |
| 155814 | 155986 | |
| 155815 | 155987 | assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); |
| 155816 | 155988 | testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); |
| 155817 | 155989 | testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt); |
| @@ -156814,15 +156986,25 @@ | ||
| 156814 | 156986 | ** SQLITE_LIMIT_COMPOUND_SELECT. |
| 156815 | 156987 | */ |
| 156816 | 156988 | static void parserDoubleLinkSelect(Parse *pParse, Select *p){ |
| 156817 | 156989 | assert( p!=0 ); |
| 156818 | 156990 | 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){ | |
| 156822 | 156994 | pLoop->pNext = pNext; |
| 156823 | 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 | + } | |
| 156824 | 157006 | } |
| 156825 | 157007 | if( (p->selFlags & SF_MultiValue)==0 && |
| 156826 | 157008 | (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 && |
| 156827 | 157009 | cnt>mxSelect |
| 156828 | 157010 | ){ |
| @@ -166129,11 +166311,11 @@ | ||
| 166129 | 166311 | ){ |
| 166130 | 166312 | #ifdef SQLITE_OMIT_WAL |
| 166131 | 166313 | return SQLITE_OK; |
| 166132 | 166314 | #else |
| 166133 | 166315 | int rc; /* Return code */ |
| 166134 | - int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ | |
| 166316 | + int iDb; /* Schema to checkpoint */ | |
| 166135 | 166317 | |
| 166136 | 166318 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 166137 | 166319 | if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; |
| 166138 | 166320 | #endif |
| 166139 | 166321 | |
| @@ -166152,10 +166334,12 @@ | ||
| 166152 | 166334 | } |
| 166153 | 166335 | |
| 166154 | 166336 | sqlite3_mutex_enter(db->mutex); |
| 166155 | 166337 | if( zDb && zDb[0] ){ |
| 166156 | 166338 | iDb = sqlite3FindDbName(db, zDb); |
| 166339 | + }else{ | |
| 166340 | + iDb = SQLITE_MAX_DB; /* This means process all schemas */ | |
| 166157 | 166341 | } |
| 166158 | 166342 | if( iDb<0 ){ |
| 166159 | 166343 | rc = SQLITE_ERROR; |
| 166160 | 166344 | sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); |
| 166161 | 166345 | }else{ |
| @@ -166200,11 +166384,11 @@ | ||
| 166200 | 166384 | ** |
| 166201 | 166385 | ** The mutex on database handle db should be held by the caller. The mutex |
| 166202 | 166386 | ** associated with the specific b-tree being checkpointed is taken by |
| 166203 | 166387 | ** this function while the checkpoint is running. |
| 166204 | 166388 | ** |
| 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 | |
| 166206 | 166390 | ** checkpointed. If an error is encountered it is returned immediately - |
| 166207 | 166391 | ** no attempt is made to checkpoint any remaining databases. |
| 166208 | 166392 | ** |
| 166209 | 166393 | ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART |
| 166210 | 166394 | ** or TRUNCATE. |
| @@ -166215,13 +166399,15 @@ | ||
| 166215 | 166399 | int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ |
| 166216 | 166400 | |
| 166217 | 166401 | assert( sqlite3_mutex_held(db->mutex) ); |
| 166218 | 166402 | assert( !pnLog || *pnLog==-1 ); |
| 166219 | 166403 | assert( !pnCkpt || *pnCkpt==-1 ); |
| 166404 | + testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */ | |
| 166405 | + testcase( iDb==SQLITE_MAX_DB ); | |
| 166220 | 166406 | |
| 166221 | 166407 | 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 ){ | |
| 166223 | 166409 | rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); |
| 166224 | 166410 | pnLog = 0; |
| 166225 | 166411 | pnCkpt = 0; |
| 166226 | 166412 | if( rc==SQLITE_BUSY ){ |
| 166227 | 166413 | bBusy = 1; |
| @@ -174914,13 +175100,13 @@ | ||
| 174914 | 175100 | res = fts3PoslistNearMerge( |
| 174915 | 175101 | &pOut, aTmp, nParam1, nParam2, paPoslist, &p2 |
| 174916 | 175102 | ); |
| 174917 | 175103 | if( res ){ |
| 174918 | 175104 | 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 ){ | |
| 174920 | 175107 | assert( pPhrase->doclist.pList[nNew]=='\0' ); |
| 174921 | - assert( nNew<=pPhrase->doclist.nList && nNew>0 ); | |
| 174922 | 175108 | memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); |
| 174923 | 175109 | pPhrase->doclist.nList = nNew; |
| 174924 | 175110 | } |
| 174925 | 175111 | *paPoslist = pPhrase->doclist.pList; |
| 174926 | 175112 | *pnToken = pPhrase->nToken; |
| @@ -176850,10 +177036,15 @@ | ||
| 176850 | 177036 | |
| 176851 | 177037 | if( sqlite3_fts3_enable_parentheses ){ |
| 176852 | 177038 | if( *zInput=='(' ){ |
| 176853 | 177039 | int nConsumed = 0; |
| 176854 | 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 | |
| 176855 | 177046 | rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed); |
| 176856 | 177047 | *pnConsumed = (int)(zInput - z) + 1 + nConsumed; |
| 176857 | 177048 | return rc; |
| 176858 | 177049 | }else if( *zInput==')' ){ |
| 176859 | 177050 | pParse->nNest--; |
| @@ -184253,31 +184444,30 @@ | ||
| 184253 | 184444 | if( pNode->block.a){ |
| 184254 | 184445 | rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n); |
| 184255 | 184446 | while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader); |
| 184256 | 184447 | blobGrowBuffer(&pNode->key, reader.term.n, &rc); |
| 184257 | 184448 | 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 ){ | |
| 184261 | 184451 | 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); | |
| 184279 | 184469 | } |
| 184280 | 184470 | } |
| 184281 | 184471 | } |
| 184282 | 184472 | nodeReaderRelease(&reader); |
| 184283 | 184473 | } |
| @@ -187755,10 +187945,11 @@ | ||
| 187755 | 187945 | memset(pCsr, 0, sizeof(unicode_cursor)); |
| 187756 | 187946 | |
| 187757 | 187947 | pCsr->aInput = (const unsigned char *)aInput; |
| 187758 | 187948 | if( aInput==0 ){ |
| 187759 | 187949 | pCsr->nInput = 0; |
| 187950 | + pCsr->aInput = (const unsigned char*)""; | |
| 187760 | 187951 | }else if( nInput<0 ){ |
| 187761 | 187952 | pCsr->nInput = (int)strlen(aInput); |
| 187762 | 187953 | }else{ |
| 187763 | 187954 | pCsr->nInput = nInput; |
| 187764 | 187955 | } |
| @@ -224323,21 +224514,26 @@ | ||
| 224323 | 224514 | pIter->nPoslist = ((int)(p[0])) >> 1; |
| 224324 | 224515 | pIter->nSize = 1; |
| 224325 | 224516 | } |
| 224326 | 224517 | |
| 224327 | 224518 | pIter->aPoslist = p; |
| 224519 | + if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){ | |
| 224520 | + pIter->aPoslist = 0; | |
| 224521 | + } | |
| 224328 | 224522 | } |
| 224329 | 224523 | } |
| 224330 | 224524 | |
| 224331 | 224525 | static void fts5DoclistIterInit( |
| 224332 | 224526 | Fts5Buffer *pBuf, |
| 224333 | 224527 | Fts5DoclistIter *pIter |
| 224334 | 224528 | ){ |
| 224335 | 224529 | 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 | + } | |
| 224339 | 224535 | } |
| 224340 | 224536 | |
| 224341 | 224537 | #if 0 |
| 224342 | 224538 | /* |
| 224343 | 224539 | ** Append a doclist to buffer pBuf. |
| @@ -225095,12 +225291,13 @@ | ||
| 225095 | 225291 | ** Return the current term. |
| 225096 | 225292 | */ |
| 225097 | 225293 | static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ |
| 225098 | 225294 | int n; |
| 225099 | 225295 | const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); |
| 225296 | + assert_nc( z || n<=1 ); | |
| 225100 | 225297 | *pn = n-1; |
| 225101 | - return &z[1]; | |
| 225298 | + return (z ? &z[1] : 0); | |
| 225102 | 225299 | } |
| 225103 | 225300 | |
| 225104 | 225301 | /* |
| 225105 | 225302 | ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). |
| 225106 | 225303 | */ |
| @@ -228382,11 +228579,12 @@ | ||
| 228382 | 228579 | ){ |
| 228383 | 228580 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 228384 | 228581 | int n; |
| 228385 | 228582 | int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); |
| 228386 | 228583 | 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); | |
| 228388 | 228586 | *piCol = 0; |
| 228389 | 228587 | *piOff = 0; |
| 228390 | 228588 | fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); |
| 228391 | 228589 | } |
| 228392 | 228590 | return rc; |
| @@ -228441,19 +228639,21 @@ | ||
| 228441 | 228639 | pIter->a = &pSorter->aPoslist[i1]; |
| 228442 | 228640 | }else{ |
| 228443 | 228641 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); |
| 228444 | 228642 | } |
| 228445 | 228643 | 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); | |
| 228447 | 228646 | *piCol = 0; |
| 228448 | 228647 | fts5ApiPhraseNextColumn(pCtx, pIter, piCol); |
| 228449 | 228648 | } |
| 228450 | 228649 | }else{ |
| 228451 | 228650 | int n; |
| 228452 | 228651 | rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); |
| 228453 | 228652 | 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); | |
| 228455 | 228655 | if( n<=0 ){ |
| 228456 | 228656 | *piCol = -1; |
| 228457 | 228657 | }else if( pIter->a[0]==0x01 ){ |
| 228458 | 228658 | pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol); |
| 228459 | 228659 | }else{ |
| @@ -228927,11 +229127,11 @@ | ||
| 228927 | 229127 | assert( nArg>0 ); |
| 228928 | 229128 | rc = SQLITE_ERROR; |
| 228929 | 229129 | *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]); |
| 228930 | 229130 | }else{ |
| 228931 | 229131 | 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 | |
| 228933 | 229133 | ); |
| 228934 | 229134 | pConfig->pTokApi = &pMod->x; |
| 228935 | 229135 | if( rc!=SQLITE_OK ){ |
| 228936 | 229136 | if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor"); |
| 228937 | 229137 | }else{ |
| @@ -228990,11 +229190,11 @@ | ||
| 228990 | 229190 | int nArg, /* Number of args */ |
| 228991 | 229191 | sqlite3_value **apUnused /* Function arguments */ |
| 228992 | 229192 | ){ |
| 228993 | 229193 | assert( nArg==0 ); |
| 228994 | 229194 | 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); | |
| 228996 | 229196 | } |
| 228997 | 229197 | |
| 228998 | 229198 | /* |
| 228999 | 229199 | ** Return true if zName is the extension on one of the shadow tables used |
| 229000 | 229200 | ** by this module. |
| @@ -233916,12 +234116,12 @@ | ||
| 233916 | 234116 | } |
| 233917 | 234117 | #endif /* SQLITE_CORE */ |
| 233918 | 234118 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 233919 | 234119 | |
| 233920 | 234120 | /************** End of stmt.c ************************************************/ |
| 233921 | -#if __LINE__!=233921 | |
| 234121 | +#if __LINE__!=234121 | |
| 233922 | 234122 | #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" | |
| 233924 | 234124 | #endif |
| 233925 | 234125 | /* Return the source-id for this library */ |
| 233926 | 234126 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 233927 | 234127 | /************************** End of sqlite3.c ******************************/ |
| 233928 | 234128 |
| --- 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(©); |
| 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 @@ | ||
| 123 | 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | 125 | */ |
| 126 | 126 | #define SQLITE_VERSION "3.35.0" |
| 127 | 127 | #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" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| @@ -2130,11 +2130,17 @@ | ||
| 2130 | 2130 | ** The first argument is an integer which is 0 to disable views, |
| 2131 | 2131 | ** positive to enable views or negative to leave the setting unchanged. |
| 2132 | 2132 | ** The second parameter is a pointer to an integer into which |
| 2133 | 2133 | ** is written 0 or 1 to indicate whether views are disabled or enabled |
| 2134 | 2134 | ** 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> | |
| 2136 | 2142 | ** |
| 2137 | 2143 | ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] |
| 2138 | 2144 | ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> |
| 2139 | 2145 | ** <dd> ^This option is used to enable or disable the |
| 2140 | 2146 | ** [fts3_tokenizer()] function which is part of the |
| @@ -10554,22 +10560,27 @@ | ||
| 10554 | 10560 | ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator |
| 10555 | 10561 | ** created by [sqlite3changeset_start()]. In the latter case, the most recent |
| 10556 | 10562 | ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this |
| 10557 | 10563 | ** is not the case, this function returns [SQLITE_MISUSE]. |
| 10558 | 10564 | ** |
| 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 | |
| 10566 | 10579 | ** is an indirect change, or false (0) otherwise. See the documentation for |
| 10567 | 10580 | ** [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. | |
| 10571 | 10582 | ** |
| 10572 | 10583 | ** If no error occurs, SQLITE_OK is returned. If an error does occur, an |
| 10573 | 10584 | ** SQLite error code is returned. The values of the output variables may not |
| 10574 | 10585 | ** be trusted in this case. |
| 10575 | 10586 | */ |
| 10576 | 10587 |
| --- 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 @@ | ||
| 366 | 366 | va_end(ap); |
| 367 | 367 | } |
| 368 | 368 | } |
| 369 | 369 | |
| 370 | 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. | |
| 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. | |
| 374 | 379 | */ |
| 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); | |
| 400 | 419 | } |
| 401 | 420 | |
| 402 | 421 | /* |
| 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. | |
| 404 | 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. | |
| 405 | 427 | */ |
| 406 | 428 | 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); | |
| 412 | 440 | } |
| 413 | 441 | |
| 414 | 442 | /* |
| 415 | 443 | ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js |
| 416 | 444 | ** Javascript module, and generates HTML elements with the following IDs: |
| @@ -519,10 +547,11 @@ | ||
| 519 | 547 | ** default is used instead: |
| 520 | 548 | ** |
| 521 | 549 | ** default-src 'self' data:; |
| 522 | 550 | ** script-src 'self' 'nonce-$nonce'; |
| 523 | 551 | ** style-src 'self' 'unsafe-inline'; |
| 552 | +** img-src * data:; | |
| 524 | 553 | ** |
| 525 | 554 | ** The text '$nonce' is replaced by style_nonce() if and whereever it |
| 526 | 555 | ** occurs in the input string. |
| 527 | 556 | ** |
| 528 | 557 | ** The string returned is obtained from fossil_malloc() and |
| @@ -530,11 +559,12 @@ | ||
| 530 | 559 | */ |
| 531 | 560 | char *style_csp(int toHeader){ |
| 532 | 561 | static const char zBackupCSP[] = |
| 533 | 562 | "default-src 'self' data:; " |
| 534 | 563 | "script-src 'self' 'nonce-$nonce'; " |
| 535 | - "style-src 'self' 'unsafe-inline'"; | |
| 564 | + "style-src 'self' 'unsafe-inline'; " | |
| 565 | + "img-src * data:"; | |
| 536 | 566 | const char *zFormat; |
| 537 | 567 | Blob csp; |
| 538 | 568 | char *zNonce; |
| 539 | 569 | char *zCsp; |
| 540 | 570 | int i; |
| @@ -609,11 +639,11 @@ | ||
| 609 | 639 | @ Tags /taglist o wideonly |
| 610 | 640 | @ Forum /forum {@2 3 4 5 6} wideonly |
| 611 | 641 | @ Chat /chat C wideonly |
| 612 | 642 | @ Tickets /ticket r wideonly |
| 613 | 643 | @ Wiki /wiki j wideonly |
| 614 | -@ Setup /setup s desktoponly | |
| 644 | +@ Admin /setup {a s} desktoponly | |
| 615 | 645 | @ Logout /logout L wideonly |
| 616 | 646 | @ Login /login !L wideonly |
| 617 | 647 | ; |
| 618 | 648 | |
| 619 | 649 | /* |
| @@ -705,11 +735,11 @@ | ||
| 705 | 735 | Th_Store("release_version", RELEASE_VERSION); |
| 706 | 736 | Th_Store("manifest_version", MANIFEST_VERSION); |
| 707 | 737 | Th_Store("manifest_date", MANIFEST_DATE); |
| 708 | 738 | Th_Store("compiler_name", COMPILER_NAME); |
| 709 | 739 | Th_Store("mainmenu", style_get_mainmenu()); |
| 710 | - url_var("stylesheet", "css", "style.css"); | |
| 740 | + stylesheet_url_var(); | |
| 711 | 741 | image_url_var("logo"); |
| 712 | 742 | image_url_var("background"); |
| 713 | 743 | if( !login_is_nobody() ){ |
| 714 | 744 | Th_Store("login", g.zLogin); |
| 715 | 745 | } |
| @@ -1011,10 +1041,14 @@ | ||
| 1011 | 1041 | if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){ |
| 1012 | 1042 | style_load_all_js_files(); |
| 1013 | 1043 | @ </body> |
| 1014 | 1044 | @ </html> |
| 1015 | 1045 | } |
| 1046 | + /* Update the user display prefs cookie if it was modified during | |
| 1047 | + ** this request. | |
| 1048 | + */ | |
| 1049 | + cookie_render(); | |
| 1016 | 1050 | } |
| 1017 | 1051 | |
| 1018 | 1052 | /* |
| 1019 | 1053 | ** Begin a side-box on the right-hand side of a page. The title and |
| 1020 | 1054 | ** the width of the box are given as arguments. The width is usually |
| 1021 | 1055 |
| --- 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 @@ | ||
| 755 | 755 | |
| 756 | 756 | style_header("Tagged Check-ins"); |
| 757 | 757 | style_submenu_element("List", "taglist"); |
| 758 | 758 | login_anonymous_available(); |
| 759 | 759 | timeline_ss_submenu(); |
| 760 | - cookie_render(); | |
| 761 | 760 | @ <h2>Check-ins with non-propagating tags:</h2> |
| 762 | 761 | blob_append(&sql, timeline_query_for_www(), -1); |
| 763 | 762 | blob_append_sql(&sql, |
| 764 | 763 | "AND blob.rid IN (SELECT rid FROM tagxref" |
| 765 | 764 | " WHERE tagtype=1 AND srcid>0" |
| 766 | 765 |
| --- 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 @@ | ||
| 425 | 425 | ** COMMAND: test-tarball |
| 426 | 426 | ** |
| 427 | 427 | ** Generate a GZIP-compressed tarball in the file given by the first argument |
| 428 | 428 | ** that contains files given in the second and subsequent arguments. |
| 429 | 429 | ** |
| 430 | -** -h, --dereference Follow symlinks; archive the files they point to. | |
| 430 | +** -h|--dereference Follow symlinks and archive the files they point to | |
| 431 | 431 | */ |
| 432 | 432 | void test_tarball_cmd(void){ |
| 433 | 433 | int i; |
| 434 | 434 | Blob zip; |
| 435 | 435 | int eFType = SymFILE; |
| 436 | 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; 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 |
-1
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1754,11 +1754,10 @@ | ||
| 1754 | 1754 | cgi_set_parameter("y", zType); |
| 1755 | 1755 | } |
| 1756 | 1756 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1757 | 1757 | cookie_write_parameter("y","y",zType); |
| 1758 | 1758 | } |
| 1759 | - cookie_render(); | |
| 1760 | 1759 | |
| 1761 | 1760 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1762 | 1761 | if( P("cf")!=0 ){ |
| 1763 | 1762 | zCirca = db_text(0, |
| 1764 | 1763 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| 1765 | 1764 |
| --- 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 @@ | ||
| 1239 | 1239 | ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS? |
| 1240 | 1240 | ** |
| 1241 | 1241 | ** Options: |
| 1242 | 1242 | ** -l|--limit LIMITCHAR |
| 1243 | 1243 | ** -q|--quote |
| 1244 | -** -R|--repository FILE | |
| 1244 | +** -R|--repository REPO | |
| 1245 | 1245 | ** |
| 1246 | 1246 | ** Run the ticket report, identified by the report format title |
| 1247 | 1247 | ** used in the GUI. The data is written as flat file on stdout, |
| 1248 | 1248 | ** using TAB as separator. The separator can be changed using |
| 1249 | 1249 | ** the -l or --limit option. |
| 1250 | 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 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 @@ | ||
| 462 | 462 | ** |
| 463 | 463 | ** Future versions of Fossil might add new commands to the set of commands |
| 464 | 464 | ** that are undoable. |
| 465 | 465 | ** |
| 466 | 466 | ** 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 | |
| 468 | 468 | ** |
| 469 | 469 | ** See also: [[commit]], [[status]] |
| 470 | 470 | */ |
| 471 | 471 | void undo_cmd(void){ |
| 472 | 472 | int isRedo = g.argv[1][0]=='r'; |
| 473 | 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/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 |
+1
-1
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -291,11 +291,11 @@ | ||
| 291 | 291 | ** |
| 292 | 292 | ** Options: |
| 293 | 293 | ** |
| 294 | 294 | ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", |
| 295 | 295 | ** "edit", "remove", and "touch" subcommands. |
| 296 | -** -R|--repository FILE Use FILE as the repository | |
| 296 | +** -R|--repository REPO Use FILE as the repository | |
| 297 | 297 | */ |
| 298 | 298 | void unversioned_cmd(void){ |
| 299 | 299 | const char *zCmd; |
| 300 | 300 | int nCmd; |
| 301 | 301 | const char *zMtime = find_option("mtime", 0, 1); |
| 302 | 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 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 @@ | ||
| 326 | 326 | } |
| 327 | 327 | |
| 328 | 328 | /* |
| 329 | 329 | ** COMMAND: user* |
| 330 | 330 | ** |
| 331 | -** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? | |
| 331 | +** Usage: %fossil user SUBCOMMAND ... ?-R|--repository REPO? | |
| 332 | 332 | ** |
| 333 | 333 | ** Run various subcommands on users of the open repository or of |
| 334 | 334 | ** the repository identified by the -R or --repository option. |
| 335 | 335 | ** |
| 336 | 336 | ** > fossil user capabilities USERNAME ?STRING? |
| 337 | 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 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 @@ | ||
| 768 | 768 | ** |
| 769 | 769 | ** Output JSON format: |
| 770 | 770 | ** |
| 771 | 771 | ** { name: "page name", |
| 772 | 772 | ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| 773 | -** mimetype: "mime type", | |
| 773 | +** mimetype: "mimetype", | |
| 774 | 774 | ** version: UUID string or null for a sandbox page, |
| 775 | 775 | ** parent: "parent uuid" or null if no parent, |
| 776 | 776 | ** isDeleted: true if the page has no content (is "deleted") |
| 777 | 777 | ** else not set (making it "falsy" in JS), |
| 778 | 778 | ** content: "page content" (only if includeContent is true) |
| @@ -836,11 +836,11 @@ | ||
| 836 | 836 | ** Ajax route handler for /wikiajax/save. |
| 837 | 837 | ** |
| 838 | 838 | ** URL params: |
| 839 | 839 | ** |
| 840 | 840 | ** page = the wiki page name. |
| 841 | -** mimetype = content mime type. | |
| 841 | +** mimetype = content mimetype. | |
| 842 | 842 | ** content = page content. Fossil considers an empty page to |
| 843 | 843 | ** be "deleted". |
| 844 | 844 | ** isnew = 1 if the page is to be newly-created, else 0 or |
| 845 | 845 | ** not send. |
| 846 | 846 | ** |
| @@ -2180,11 +2180,11 @@ | ||
| 2180 | 2180 | if( rid>0 ){ |
| 2181 | 2181 | pWiki = manifest_get(rid, CFTYPE_EVENT, 0); |
| 2182 | 2182 | } |
| 2183 | 2183 | } |
| 2184 | 2184 | 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. */ | |
| 2186 | 2186 | if(isSandbox){ |
| 2187 | 2187 | zMimeType = |
| 2188 | 2188 | wiki_filter_mimetypes(db_get("sandbox-mimetype", |
| 2189 | 2189 | "text/x-fossil-wiki")); |
| 2190 | 2190 | }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ |
| 2191 | 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: "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 @@ | ||
| 768 | 768 | ** |
| 769 | 769 | ** Output JSON format: |
| 770 | 770 | ** |
| 771 | 771 | ** { name: "page name", |
| 772 | 772 | ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| 773 | -** mimetype: "mime type", | |
| 773 | +** mimetype: "mimetype", | |
| 774 | 774 | ** version: UUID string or null for a sandbox page, |
| 775 | 775 | ** parent: "parent uuid" or null if no parent, |
| 776 | 776 | ** isDeleted: true if the page has no content (is "deleted") |
| 777 | 777 | ** else not set (making it "falsy" in JS), |
| 778 | 778 | ** content: "page content" (only if includeContent is true) |
| @@ -836,11 +836,11 @@ | ||
| 836 | 836 | ** Ajax route handler for /wikiajax/save. |
| 837 | 837 | ** |
| 838 | 838 | ** URL params: |
| 839 | 839 | ** |
| 840 | 840 | ** page = the wiki page name. |
| 841 | -** mimetype = content mime type. | |
| 841 | +** mimetype = content mimetype. | |
| 842 | 842 | ** content = page content. Fossil considers an empty page to |
| 843 | 843 | ** be "deleted". |
| 844 | 844 | ** isnew = 1 if the page is to be newly-created, else 0 or |
| 845 | 845 | ** not send. |
| 846 | 846 | ** |
| @@ -2180,11 +2180,11 @@ | ||
| 2180 | 2180 | if( rid>0 ){ |
| 2181 | 2181 | pWiki = manifest_get(rid, CFTYPE_EVENT, 0); |
| 2182 | 2182 | } |
| 2183 | 2183 | } |
| 2184 | 2184 | 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. */ | |
| 2186 | 2186 | if(isSandbox){ |
| 2187 | 2187 | zMimeType = |
| 2188 | 2188 | wiki_filter_mimetypes(db_get("sandbox-mimetype", |
| 2189 | 2189 | "text/x-fossil-wiki")); |
| 2190 | 2190 | }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ |
| 2191 | 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: "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 @@ | ||
| 972 | 972 | ** -P|--port TCPPORT |
| 973 | 973 | ** |
| 974 | 974 | ** Specifies the TCP port (default port is 8080) on which the |
| 975 | 975 | ** server should listen. |
| 976 | 976 | ** |
| 977 | -** -R|--repository REPOSITORY | |
| 977 | +** -R|--repository REPO | |
| 978 | 978 | ** |
| 979 | 979 | ** Specifies the name of the repository to be served. |
| 980 | 980 | ** The repository option may be omitted if the working directory |
| 981 | 981 | ** is within an open checkout. |
| 982 | 982 | ** The REPOSITORY can be a directory (aka folder) that contains |
| 983 | 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 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 @@ | ||
| 2 | 2 | |
| 3 | 3 | This file describes the testing procedures for Fossil prior to an |
| 4 | 4 | official release. |
| 5 | 5 | |
| 6 | 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 | + | |
| 7 | 12 | <li><p> |
| 8 | 13 | From a private directory (not the source tree) run |
| 9 | 14 | "<b>tclsh $SRC/test/tester.tcl $FOSSIL</b>" where $FOSSIL is the |
| 10 | 15 | name of the executable under test and $SRC is the source tree. |
| 11 | 16 | Verify that there are no errors. |
| @@ -91,10 +96,17 @@ | ||
| 91 | 96 | |
| 92 | 97 | <li><p> |
| 93 | 98 | Use the release candidate version of fossil in production on the |
| 94 | 99 | [http://fossil-scm.org/] website for at least 48 hours (without |
| 95 | 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> | |
| 96 | 108 | |
| 97 | 109 | <li><p> |
| 98 | 110 | Verify that the [../www/changes.wiki | Change Log] is correct and |
| 99 | 111 | up-to-date. |
| 100 | 112 | </ol> |
| 101 | 113 |
| --- 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 |
+1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -634,10 +634,11 @@ | ||
| 634 | 634 | $(SRCDIR)/fossil.bootstrap.js \ |
| 635 | 635 | $(SRCDIR)/fossil.confirmer.js \ |
| 636 | 636 | $(SRCDIR)/fossil.copybutton.js \ |
| 637 | 637 | $(SRCDIR)/fossil.dom.js \ |
| 638 | 638 | $(SRCDIR)/fossil.fetch.js \ |
| 639 | + $(SRCDIR)/fossil.info-diff.js \ | |
| 639 | 640 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 640 | 641 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 641 | 642 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 642 | 643 | $(SRCDIR)/fossil.page.pikchrshow.js \ |
| 643 | 644 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 644 | 645 |
| --- 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 |
+2
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -559,10 +559,11 @@ | ||
| 559 | 559 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 560 | 560 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 561 | 561 | "$(SRCDIR)\fossil.copybutton.js" \ |
| 562 | 562 | "$(SRCDIR)\fossil.dom.js" \ |
| 563 | 563 | "$(SRCDIR)\fossil.fetch.js" \ |
| 564 | + "$(SRCDIR)\fossil.info-diff.js" \ | |
| 564 | 565 | "$(SRCDIR)\fossil.numbered-lines.js" \ |
| 565 | 566 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 566 | 567 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 567 | 568 | "$(SRCDIR)\fossil.page.pikchrshow.js" \ |
| 568 | 569 | "$(SRCDIR)\fossil.page.wikiedit.js" \ |
| @@ -1164,10 +1165,11 @@ | ||
| 1164 | 1165 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1165 | 1166 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1166 | 1167 | echo "$(SRCDIR)\fossil.copybutton.js" >> $@ |
| 1167 | 1168 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1168 | 1169 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1170 | + echo "$(SRCDIR)\fossil.info-diff.js" >> $@ | |
| 1169 | 1171 | echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ |
| 1170 | 1172 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1171 | 1173 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1172 | 1174 | echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@ |
| 1173 | 1175 | echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ |
| 1174 | 1176 |
| --- 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 @@ | ||
| 231 | 231 | |
| 232 | 232 | This requires OpenSSL 1.1 or higher. If you’re on 1.0 or older, you |
| 233 | 233 | won’t have the `-pbkdf2` and `-iter` options, and you may have to choose |
| 234 | 234 | a different cipher algorithm; both changes are likely to weaken the |
| 235 | 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: | |
| 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”) | |
| 241 | 249 | |
| 242 | - /usr/local/Cellar/openssl\@1.1/1.1.1g/bin/openssl ... | |
| 250 | +[lssl]: https://www.libressl.org/ | |
| 243 | 251 | |
| 244 | 252 | |
| 245 | 253 | ## <a id="rest"></a> Restoring From An Encrypted Backup |
| 246 | 254 | |
| 247 | 255 | The “restore” script for the above fragment is basically an inverse of |
| 248 | 256 |
| --- 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 @@ | ||
| 36 | 36 | the form "<b>property: argument ...</b>". |
| 37 | 37 | The remainder of this document describes the available properties and |
| 38 | 38 | their arguments. |
| 39 | 39 | |
| 40 | 40 | <hr> |
| 41 | + | |
| 41 | 42 | <h2 id="repository">repository: <i>PATH</i></h2> |
| 42 | 43 | |
| 43 | 44 | This property defines the Fossil repository that the server will use. |
| 44 | 45 | Every Fossil CGI requires either this property or the |
| 45 | 46 | [#directory|<b>directory:</b>] property (but not both). |
| 46 | 47 | Many Fossil CGI scripts have this one property and no other. |
| 48 | + | |
| 47 | 49 | |
| 48 | 50 | <h2 id="directory">directory: <i>PATH</i></h2> |
| 49 | 51 | |
| 50 | 52 | The PATH is the name of a directory that contains one or more Fossil |
| 51 | 53 | repository files having the suffix ".fossil". If this property is used |
| @@ -52,23 +54,16 @@ | ||
| 52 | 54 | instead of [#repository|<b>repository:</b>], then the Fossil server is |
| 53 | 55 | able to serve all of the repositories in the directory. The specific |
| 54 | 56 | repository used is selected by a prefix on the PATH_INFO. |
| 55 | 57 | |
| 56 | 58 | |
| 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 | 59 | <h2 id="notfound">notfound: <i>URL</i></h2> |
| 66 | 60 | |
| 67 | 61 | If the [#directory|<b>directory:</b>] option is used and if the PATH_INFO |
| 68 | 62 | of the HTTP request does not correspond to any Fossil repository, then |
| 69 | 63 | the request redirects to URL. |
| 64 | + | |
| 70 | 65 | |
| 71 | 66 | <h2 id="repolist">repolist</h2> |
| 72 | 67 | |
| 73 | 68 | This is a Boolean property. |
| 74 | 69 | If it is present, and if the [#directory:|<b>directory:</b>] option is used, |
| @@ -78,10 +73,75 @@ | ||
| 78 | 73 | The "skin" of the reply is determined by the first |
| 79 | 74 | repository in the list that has a non-zero |
| 80 | 75 | [/help?cmd=repolist-skin|repolist-skin] setting. |
| 81 | 76 | If no repository has such a non-zero repolist-skin setting, then |
| 82 | 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 | + | |
| 83 | 143 | |
| 84 | 144 | <h2 id="extroot">extroot: <i>PATH</i></h2> |
| 85 | 145 | |
| 86 | 146 | This property defines the DOCUMENT_ROOT for the |
| 87 | 147 | [./serverext.wiki|CGI Server Extensions]. If this property |
| @@ -93,52 +153,24 @@ | ||
| 93 | 153 | extension. The sub-CGI extension outputs reply text, when Fossil |
| 94 | 154 | then (optionally) augments with its own header and footer and returns |
| 95 | 155 | to the original requestor. The property controls the DOCUMENT_ROOT |
| 96 | 156 | of the sub-CGI. |
| 97 | 157 | |
| 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 | + | |
| 140 | 172 | |
| 141 | 173 | <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> |
| 142 | 174 | |
| 143 | 175 | This parameter causes the contents of the given file to override the |
| 144 | 176 | site's <tt>mainmenu</tt> configuration setting, much in the same way |
| @@ -145,11 +177,5 @@ | ||
| 145 | 177 | that the <tt>skin</tt> setting overrides the skin. This can be used to |
| 146 | 178 | apply a common main menu to a number of sites, and centrally maintain |
| 147 | 179 | it, without having to copy its contents into each site. Note, however, |
| 148 | 180 | that the contents of this setting are not stored in the repository and |
| 149 | 181 | 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 | 182 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -36,16 +36,18 @@ | |
| 36 | the form "<b>property: argument ...</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: argument ...</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 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <a name='v2_15'></a> |
| 4 | 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 | |
| 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 | |
| 7 | 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. | |
| 8 | 21 | * 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 | |
| 10 | 24 | "sitemap-*" settings. The older settings are no longer used. |
| 11 | 25 | <b>This change might require minor server configuration |
| 12 | 26 | adjustments on servers that use /sitemap extensions.</b> |
| 13 | 27 | The /Admin/Configuration page provides the ability to edit |
| 14 | 28 | the new "sitemap-extra" setting. |
| @@ -17,30 +31,50 @@ | ||
| 17 | 31 | [/help?cmd=http|fossil http]. This option causes Fossil to |
| 18 | 32 | understand URIs of the form "/doc/NAME/..." as if they were |
| 19 | 33 | "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of |
| 20 | 34 | [./embeddeddoc.wiki|embedded documentation] changes prior to |
| 21 | 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. | |
| 22 | 46 | * The "pikchr-background" settings is now available in |
| 23 | 47 | "detail.txt" skin files, for better control of Pikchr |
| 24 | 48 | 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 | |
| 30 | 50 | [/help?cmd=tarball|tarball], |
| 31 | 51 | [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar] |
| 32 | 52 | commands. |
| 33 | 53 | * The javascript used to implement the hamburger menu on the |
| 34 | 54 | default built-in skin has been made generic so that it is usable |
| 35 | 55 | by a variety of skins, and promoted to an ordinary built-in |
| 36 | 56 | 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. | |
| 42 | 76 | |
| 43 | 77 | <a name='v2_14'></a> |
| 44 | 78 | <h2>Changes for Version 2.14 (2021-01-20)</h2> |
| 45 | 79 | |
| 46 | 80 | * <b>Schema Update Notice #1:</b> |
| 47 | 81 |
| --- 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 @@ | ||
| 94 | 94 | ### Deletion of Messages |
| 95 | 95 | |
| 96 | 96 | Any user may *locally* delete a given message by clicking on the "tab" |
| 97 | 97 | at the top of the message and clicking the button which appears. Such |
| 98 | 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. | |
| 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. | |
| 103 | 104 | |
| 104 | 105 | ## Implementation Details |
| 105 | 106 | |
| 106 | 107 | *You do not need to understand how Fossil chat works in order to use it. |
| 107 | 108 | But many developers prefer to know how their tools work. |
| 108 | 109 |
| --- 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 |
+18
-1
| --- www/customskin.md | ||
| +++ www/customskin.md | ||
| @@ -5,11 +5,11 @@ | ||
| 5 | 5 | the look and feel (the "skin") of Fossil to better suite your own individual tastes. |
| 6 | 6 | This document provides background information to aid you in that task. |
| 7 | 7 | |
| 8 | 8 | ## <a name="builtin"></a>Built-in Skins |
| 9 | 9 | |
| 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 | |
| 11 | 11 | suite your tastes, perhaps one of the other built-in skins will work better. |
| 12 | 12 | If nothing else, the built-in skins can serve as examples or templates that |
| 13 | 13 | you can use to develop your own custom skin. |
| 14 | 14 | |
| 15 | 15 | The sources to these built-ins can |
| @@ -134,10 +134,27 @@ | ||
| 134 | 134 | Finally, Fossil always adds its own footer (unless overridden) |
| 135 | 135 | to close out the generated HTML: |
| 136 | 136 | |
| 137 | 137 | </body> |
| 138 | 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 | + | |
| 139 | 156 | |
| 140 | 157 | ## <a name="override"></a>Overriding the HTML Header and Footer |
| 141 | 158 | |
| 142 | 159 | Notice that the `<html>`, `<head>`, and opening `<body>` |
| 143 | 160 | elements at the beginning of the document, |
| 144 | 161 |
| --- 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 @@ | ||
| 1 | 1 | # The Default Content Security Policy (CSP) |
| 2 | 2 | |
| 3 | 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 | - | |
| 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. | |
| 49 | 22 | |
| 50 | 23 | ## The Default Restrictions |
| 51 | 24 | |
| 52 | -The Fossil default CSP declares the following content restrictions: | |
| 25 | +The default CSP used by Fossil is as follows: | |
| 53 | 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. | |
| 54 | 44 | |
| 55 | 45 | ### <a name="base"></a> default-src 'self' data: |
| 56 | 46 | |
| 57 | 47 | This policy means mixed-origin content isn’t allowed, so you can’t refer |
| 58 | 48 | to resources on other web domains. Browsers will ignore a link like the |
| @@ -86,10 +76,23 @@ | ||
| 86 | 76 | There are many other cases, [covered below](#serving). |
| 87 | 77 | |
| 88 | 78 | [b64]: https://en.wikipedia.org/wiki/Base64 |
| 89 | 79 | [svr]: ./server/ |
| 90 | 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 | + | |
| 91 | 94 | |
| 92 | 95 | ### <a name="style"></a> style-src 'self' 'unsafe-inline' |
| 93 | 96 | |
| 94 | 97 | This policy allows CSS information to come from separate files hosted |
| 95 | 98 | under the Fossil repo server’s Internet domain. It also allows inline CSS |
| @@ -106,10 +109,11 @@ | ||
| 106 | 109 | flexibility and the work-arounds are verbose and difficult to maintain. |
| 107 | 110 | Furthermore, the harm that can be done with style injections is far |
| 108 | 111 | less than the harm possible with injected javascript. And so the |
| 109 | 112 | `'unsafe-inline'` compromise is accepted for now, though it might |
| 110 | 113 | go away in some future release of Fossil. |
| 114 | + | |
| 111 | 115 | |
| 112 | 116 | ### <a name="script"></a> script-src 'self' 'nonce-%s' |
| 113 | 117 | |
| 114 | 118 | This policy disables in-line JavaScript and only allows `<script>` |
| 115 | 119 | elements if the `<script>` includes a `nonce` attribute that matches the |
| 116 | 120 | |
| 117 | 121 | 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 |
+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 |
| --- 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 @@ | ||
| 8 | 8 | This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org) |
| 9 | 9 | and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations |
| 10 | 10 | have an identical implementation of the Fossil data model, are 100% compatible in terms of |
| 11 | 11 | data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage. |
| 12 | 12 | |
| 13 | -## General Features | |
| 13 | +# General Features | |
| 14 | 14 | |
| 15 | -* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator | |
| 16 | 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) |
| 17 | 16 | * Allow diffing of Forum posts |
| 18 | 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 |
| 19 | 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 |
| 20 | 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 |
| 21 | 20 | * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown |
| 22 | 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) |
| 23 | 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) |
| 24 | 23 | |
| 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 | |
| 42 | 69 | |
| 43 | 70 | The Fossil SCM project uses tickets in a [somewhat unusual manner](https://fossil-scm.org/home/reportlist) |
| 44 | 71 | 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 | |
| 46 | 73 | 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. | |
| 52 | 81 | |
| 53 | 82 | Projects relating to the ticketing system include: |
| 54 | 83 | |
| 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. | |
| 56 | 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. |
| 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 | |
| 58 | 100 | |
| 59 | 101 | # Tasks Requiring Fossil Data Model Knowledge |
| 60 | 102 | |
| 61 | 103 | The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model |
| 62 | 104 | is designed to [endure for centuries](./fileformat.wiki), |
| 63 | 105 |
| --- 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 @@ | ||
| 95 | 95 | <hr> |
| 96 | 96 | <h3>Quick Start</h3> |
| 97 | 97 | |
| 98 | 98 | 1. [/uv/download.html|Download] or install using a package manager or |
| 99 | 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> | |
| 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 | 102 | 4. <tt>fossil add</tt> <i>files-or-directories</i> |
| 103 | 103 | 5. <tt>fossil commit -m</tt> "<i>commit message</i>" |
| 104 | 104 | 6. <tt>fossil ui</tt> |
| 105 | 105 | 7. Repeat steps 4, 5, and 6, in any order, as necessary. |
| 106 | 106 | See the [./quickstart.wiki|Quick Start Guide] for more detail. |
| 107 | 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>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 |
+1
-1
| --- www/makefile.wiki | ||
| +++ www/makefile.wiki | ||
| @@ -94,11 +94,11 @@ | ||
| 94 | 94 | Running this Tcl script will automatically regenerate all makefiles. |
| 95 | 95 | In order to add a new source file to the Fossil implementation, simply |
| 96 | 96 | edit makemake.tcl to add the new filename, then rerun the script, and |
| 97 | 97 | all of the makefiles for all targets will be rebuild. |
| 98 | 98 | |
| 99 | -There is an option code verification step implemented using | |
| 99 | +There is an optional code verification step implemented using | |
| 100 | 100 | |
| 101 | 101 | 15. [/file/src/codecheck1.c | codecheck1.c] |
| 102 | 102 | |
| 103 | 103 | This file implements a small utility program ("codecheck1") |
| 104 | 104 | that scans other Fossil source files looking for errors in printf-style |
| 105 | 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 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 @@ | ||
| 3 | 3 | # Run this TCL script to generate a WIKI page that contains a |
| 4 | 4 | # permuted index of the various documentation files. |
| 5 | 5 | # |
| 6 | 6 | # tclsh mkindex.tcl |
| 7 | 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 | +# | |
| 8 | 12 | |
| 9 | 13 | set doclist { |
| 10 | 14 | aboutcgi.wiki {How CGI Works In Fossil} |
| 11 | 15 | aboutdownload.wiki {How The Download Page Works} |
| 12 | 16 | adding_code.wiki {Adding New Features To Fossil} |
| @@ -41,10 +45,11 @@ | ||
| 41 | 45 | customgraph.md {Theming: Customizing the Timeline Graph} |
| 42 | 46 | customskin.md {Theming: Customizing The Appearance of Web Pages} |
| 43 | 47 | customskin.md {Custom Skins} |
| 44 | 48 | custom_ticket.wiki {Customizing The Ticket System} |
| 45 | 49 | defcsp.md {The Default Content Security Policy} |
| 50 | + delta-manifests.md {Delta Manifests} | |
| 46 | 51 | delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm} |
| 47 | 52 | delta_format.wiki {Fossil Delta Format} |
| 48 | 53 | embeddeddoc.wiki {Embedded Project Documentation} |
| 49 | 54 | encryptedrepos.wiki {How To Use Encrypted Repositories} |
| 50 | 55 | env-opts.md {Environment Variables and Global Options} |
| @@ -125,18 +130,20 @@ | ||
| 125 | 130 | } |
| 126 | 131 | foreach {file title} $doclist { |
| 127 | 132 | set n [llength $title] |
| 128 | 133 | regsub -all {\s+} $title { } title |
| 129 | 134 | 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 — $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 — $prefix" $file 0] | |
| 143 | +# } | |
| 144 | +# } | |
| 138 | 145 | } |
| 139 | 146 | set permindex [lsort -dict -index 0 $permindex] |
| 140 | 147 | set out [open permutedindex.html w] |
| 141 | 148 | fconfigure $out -encoding utf-8 -translation lf |
| 142 | 149 | puts $out \ |
| @@ -160,14 +167,14 @@ | ||
| 160 | 167 | <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> |
| 161 | 168 | <li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 162 | 169 | book</a> |
| 163 | 170 | </ul> |
| 164 | 171 | <a name="pindex"></a> |
| 165 | -<h2>Permuted Index:</h2> | |
| 172 | +<h2>Other Documents:</h2> | |
| 166 | 173 | <ul>} |
| 167 | 174 | foreach entry $permindex { |
| 168 | 175 | foreach {title file bold} $entry break |
| 169 | - if {$bold} {set title <b>$title</b>} | |
| 176 | +# if {$bold} {set title <b>$title</b>} | |
| 170 | 177 | if {[string match /* $file]} {set file ../../..$file} |
| 171 | 178 | puts $out "<li><a href=\"$file\">$title</a></li>" |
| 172 | 179 | } |
| 173 | 180 | puts $out "</ul></div>" |
| 174 | 181 |
| --- 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 — $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 — $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 |
+109
-342
| --- www/permutedindex.html | ||
| +++ www/permutedindex.html | ||
| @@ -18,349 +18,116 @@ | ||
| 18 | 18 | <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> |
| 19 | 19 | <li> <a href='http://fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 20 | 20 | book</a> |
| 21 | 21 | </ul> |
| 22 | 22 | <a name="pindex"></a> |
| 23 | -<h2>Permuted Index:</h2> | |
| 23 | +<h2>Other Documents:</h2> | |
| 24 | 24 | <ul> |
| 25 | -<li><a href="fossil-is-not-relational.md">(Non-relational) Fossil Data Model — Introduction to the</a></li> | |
| 26 | -<li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> | |
| 27 | -<li><a href="fossil-from-msvc.wiki">2010 IDE — 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 — 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 — Contributor License</a></li> | |
| 34 | -<li><a href="alerts.md">Alerts And Notifications — Email</a></li> | |
| 35 | -<li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> | |
| 36 | -<li><a href="blame.wiki">Algorithm Of Fossil — The Annotate/Blame</a></li> | |
| 37 | -<li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil — The</a></li> | |
| 38 | -<li><a href="customskin.md">Appearance of Web Pages — Theming: Customizing The</a></li> | |
| 39 | -<li><a href="hashes.md">Artifact Identification — Hashes: Fossil</a></li> | |
| 40 | -<li><a href="faq.wiki">Asked Questions — Frequently</a></li> | |
| 41 | -<li><a href="password.wiki">Authentication — 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 — The</a></li> | |
| 44 | -<li><a href="fossil_prompt.wiki">Bash Prompt — 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 — Differences</a></li> | |
| 47 | -<li><a href="hashpolicy.wiki">Between SHA1 and SHA3-256 — Hash Policy: Choosing</a></li> | |
| 48 | -<li><a href="blockchain.md">Blockchain? — Is Fossil A</a></li> | |
| 49 | -<li><a href="antibot.wiki">Bots — Defense against Spiders and</a></li> | |
| 50 | -<li><a href="private.wiki">Branches — 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 — The Fossil</a></li> | |
| 54 | -<li><a href="cap-theorem.md">CAP Theorem — Fossil and the</a></li> | |
| 55 | -<li><a href="caps/">Capabilities — Administering User</a></li> | |
| 56 | -<li><a href="caps/ref.html">Capability Reference — 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 — 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 — How</a></li> | |
| 61 | -<li><a href="changes.wiki">Changelog — Fossil</a></li> | |
| 62 | -<li><a href="chat.md">Chat — 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 — Check-in</a></li> | |
| 67 | -<li><a href="../test/release-checklist.wiki">Checklist — 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 — 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 — Hash Policy:</a></li> | |
| 73 | -<li><a href="chroot.md">Chroot Jail — Server</a></li> | |
| 74 | -<li><a href="contribute.wiki">Code or Documentation To The Fossil Project — Contributing</a></li> | |
| 75 | -<li><a href="style.wiki">Code Style Guidelines — Source</a></li> | |
| 76 | -<li><a href="../../../help">Commands and Webpages — 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 — Fossil Core</a></li> | |
| 79 | -<li><a href="cgi.wiki">Configuration Options — CGI Script</a></li> | |
| 80 | -<li><a href="server/">Configure A Fossil Server — How To</a></li> | |
| 81 | -<li><a href="rebaseharm.md">Considered Harmful — Rebase</a></li> | |
| 82 | -<li><a href="contact.md">Contact Information — Developer</a></li> | |
| 83 | -<li><a href="shunning.wiki">Content From Fossil — Shunning: Deleting</a></li> | |
| 84 | -<li><a href="defcsp.md">Content Security Policy — 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 — Benefits Of Version</a></li> | |
| 88 | -<li><a href="concepts.wiki">Core Concepts — Fossil</a></li> | |
| 89 | -<li><a href="newrepo.wiki">Create A New Fossil Repository — 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 — Questions And</a></li> | |
| 92 | -<li><a href="css-tricks.md">CSS Tips and Tricks — 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 — 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 — Theming:</a></li> | |
| 97 | -<li><a href="fossil-is-not-relational.md">Data Model — Introduction to the (Non-relational) Fossil</a></li> | |
| 98 | -<li><a href="tech_overview.wiki">Databases Used By Fossil — SQLite</a></li> | |
| 99 | -<li><a href="defcsp.md">Default Content Security Policy — 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 — Shunning:</a></li> | |
| 102 | -<li><a href="private.wiki">Deleting Private Branches — Creating, Syncing, and</a></li> | |
| 103 | -<li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm — Fossil</a></li> | |
| 104 | -<li><a href="delta_format.wiki">Delta Format — Fossil</a></li> | |
| 105 | -<li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> | |
| 106 | -<li><a href="theory1.wiki">Design Of The Fossil DVCS — 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 — Fossil</a></li> | |
| 109 | -<li><a href="pikchr.md">Diagram Language — 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 — Embedded Project</a></li> | |
| 112 | -<li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> | |
| 113 | -<li><a href="aboutdownload.wiki">Download Page Works — How The</a></li> | |
| 114 | -<li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> | |
| 115 | -<li><a href="quotes.wiki">DVCSes in General — 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 — Fossil Delta</a></li> | |
| 119 | -<li><a href="encryptedrepos.wiki">Encrypted Repositories — 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 — Webpage</a></li> | |
| 123 | -<li><a href="inout.wiki">Export To And From Git — Import And</a></li> | |
| 124 | -<li><a href="fossil-from-msvc.wiki">Express 2010 IDE — Integrating Fossil in the Microsoft</a></li> | |
| 125 | -<li><a href="serverext.wiki">Extensions — CGI Server</a></li> | |
| 126 | -<li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts — Adding</a></li> | |
| 127 | -<li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> | |
| 128 | -<li><a href="fileformat.wiki">File Format — 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 — The</a></li> | |
| 131 | -<li><a href="unvers.wiki">Files — Unversioned</a></li> | |
| 132 | -<li><a href="branching.wiki">Forking, Merging, and Tagging — Branching,</a></li> | |
| 133 | -<li><a href="delta_format.wiki">Format — Fossil Delta</a></li> | |
| 134 | -<li><a href="fileformat.wiki">Format — Fossil File</a></li> | |
| 135 | -<li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size — Image</a></li> | |
| 136 | -<li><a href="../../../md_rules">Formatting Rules — Markdown</a></li> | |
| 137 | -<li><a href="../../../wiki_rules">Formatting Rules — Wiki</a></li> | |
| 138 | -<li><a href="forum.wiki">Forums — 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 — 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 — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> | |
| 160 | -<li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> | |
| 161 | -<li><a href="inout.wiki">Git — Import And Export To And From</a></li> | |
| 162 | -<li><a href="mirrorlimitations.md">Git Mirrors — 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 — Quotes: What People Are Saying About Fossil,</a></li> | |
| 165 | -<li><a href="mirrortogithub.md">GitHub — How To Mirror A Fossil Repository On</a></li> | |
| 166 | -<li><a href="globs.md">Glob Patterns — File Name</a></li> | |
| 167 | -<li><a href="env-opts.md">Global Options — Environment Variables and</a></li> | |
| 168 | -<li><a href="customgraph.md">Graph — Theming: Customizing the Timeline</a></li> | |
| 169 | -<li><a href="grep.md">grep — Fossil grep vs POSIX</a></li> | |
| 170 | -<li><a href="grep.md">grep vs POSIX grep — Fossil</a></li> | |
| 171 | -<li><a href="hacker-howto.wiki">Guide — Fossil Developers</a></li> | |
| 172 | -<li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> | |
| 173 | -<li><a href="gitusers.md">Guide — Git to Fossil Translation</a></li> | |
| 174 | -<li><a href="style.wiki">Guidelines — 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 — 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 — Fossil Tips And Usage</a></li> | |
| 181 | -<li><a href="history.md">History Of Fossil — 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 — 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 — Hacker</a></li> | |
| 191 | -<li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> | |
| 192 | -<li><a href="hashes.md">Identification — 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 — 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 — Developer Contact</a></li> | |
| 197 | -<li><a href="build.wiki">Installing Fossil — 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 — Fossil Repository</a></li> | |
| 200 | -<li><a href="webui.wiki">Interface — 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 — Server Chroot</a></li> | |
| 205 | -<li><a href="javascript.md">JavaScript in Fossil — Use of</a></li> | |
| 206 | -<li><a href="pikchr.md">Language — The Pikchr Diagram</a></li> | |
| 207 | -<li><a href="th1.md">Language — The TH1 Scripting</a></li> | |
| 208 | -<li><a href="copyright-release.html">License Agreement — Contributor</a></li> | |
| 209 | -<li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li> | |
| 210 | -<li><a href="interwiki.md">Links — Interwiki</a></li> | |
| 211 | -<li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> | |
| 212 | -<li><a href="loadmgmt.md">Load — Managing Server</a></li> | |
| 213 | -<li><a href="password.wiki">Management And Authentication — Password</a></li> | |
| 214 | -<li><a href="loadmgmt.md"><b>Managing Server Load</b></a></li> | |
| 215 | -<li><a href="../../../sitemap">Map — 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 — The Backoffice</a></li> | |
| 218 | -<li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> | |
| 219 | -<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> | |
| 220 | -<li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> | |
| 221 | -<li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub — How To</a></li> | |
| 222 | -<li><a href="mirrorlimitations.md">Mirrors — Limitations On Git</a></li> | |
| 223 | -<li><a href="fossil-is-not-relational.md">Model — Introduction to the (Non-relational) Fossil Data</a></li> | |
| 224 | -<li><a href="globs.md">Name Glob Patterns — File</a></li> | |
| 225 | -<li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> | |
| 226 | -<li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> | |
| 227 | -<li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> | |
| 228 | -<li><a href="alerts.md">Notifications — Email Alerts And</a></li> | |
| 229 | -<li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> | |
| 230 | -<li><a href="pop.wiki">Operation — Principles Of</a></li> | |
| 231 | -<li><a href="cgi.wiki">Options — CGI Script Configuration</a></li> | |
| 232 | -<li><a href="env-opts.md">Options — Environment Variables and Global</a></li> | |
| 233 | -<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> | |
| 234 | -<li><a href="index.wiki">Page — Home</a></li> | |
| 235 | -<li><a href="fileedit-page.md">Page — The fileedit</a></li> | |
| 236 | -<li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> | |
| 237 | -<li><a href="customskin.md">Pages — 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 — File Name Glob</a></li> | |
| 240 | -<li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General — 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 — The</a></li> | |
| 243 | -<li><a href="defcsp.md">Policy — The Default Content Security</a></li> | |
| 244 | -<li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 — Hash</a></li> | |
| 245 | -<li><a href="grep.md">POSIX grep — 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 — Creating, Syncing, and Deleting</a></li> | |
| 249 | -<li><a href="makefile.wiki">Process — The Fossil Build</a></li> | |
| 250 | -<li><a href="contribute.wiki">Project — Contributing Code or Documentation To The Fossil</a></li> | |
| 251 | -<li><a href="embeddeddoc.wiki">Project Documentation — Embedded</a></li> | |
| 252 | -<li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> | |
| 253 | -<li><a href="childprojects.wiki">Projects — Child</a></li> | |
| 254 | -<li><a href="fossil_prompt.wiki">Prompt — Fossilized Bash</a></li> | |
| 255 | -<li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> | |
| 256 | -<li><a href="history.md">Purpose And History Of Fossil — The</a></li> | |
| 257 | -<li><a href="faq.wiki">Questions — 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 — 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 — User Capability</a></li> | |
| 263 | -<li><a href="backup.md">Remote Fossil Repository — Backing Up a</a></li> | |
| 264 | -<li><a href="image-format-vs-repo-size.md">Repo Size — Image Format vs Fossil</a></li> | |
| 265 | -<li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> | |
| 266 | -<li><a href="encryptedrepos.wiki">Repositories — How To Use Encrypted</a></li> | |
| 267 | -<li><a href="backup.md">Repository — Backing Up a Remote Fossil</a></li> | |
| 268 | -<li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> | |
| 269 | -<li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> | |
| 270 | -<li><a href="mirrortogithub.md">Repository On GitHub — 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 — Markdown Formatting</a></li> | |
| 273 | -<li><a href="../../../wiki_rules">Rules — Wiki Formatting</a></li> | |
| 274 | -<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Up and</a></li> | |
| 275 | -<li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> | |
| 276 | -<li><a href="cgi.wiki">Script Configuration Options — CGI</a></li> | |
| 277 | -<li><a href="th1.md">Scripting Language — The TH1</a></li> | |
| 278 | -<li><a href="serverext.wiki">Scripts — Adding Extensions To A Fossil Server Using CGI</a></li> | |
| 279 | -<li><a href="defcsp.md">Security Policy — The Default Content</a></li> | |
| 280 | -<li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> | |
| 281 | -<li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> | |
| 282 | -<li><a href="server/">Server — 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 — CGI</a></li> | |
| 285 | -<li><a href="loadmgmt.md">Server Load — Managing</a></li> | |
| 286 | -<li><a href="serverext.wiki">Server Using CGI Scripts — Adding Extensions To A Fossil</a></li> | |
| 287 | -<li><a href="settings.wiki">Settings — Fossil</a></li> | |
| 288 | -<li><a href="caps/admin-v-setup.md">Setup and Admin Users — Differences Between</a></li> | |
| 289 | -<li><a href="hashpolicy.wiki">SHA1 and SHA3-256 — Hash Policy: Choosing Between</a></li> | |
| 290 | -<li><a href="hashpolicy.wiki">SHA3-256 — 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 — 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 — Image Format vs Fossil Repo</a></li> | |
| 295 | -<li><a href="customskin.md">Skins — 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 — 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 — Using</a></li> | |
| 300 | -<li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> | |
| 301 | -<li><a href="stats.wiki">Statistics — Performance</a></li> | |
| 302 | -<li><a href="style.wiki">Style Guidelines — Source Code</a></li> | |
| 303 | -<li><a href="foss-cklist.wiki">Successful Open-Source Projects — Checklist For</a></li> | |
| 304 | -<li><a href="sync.wiki">Sync Protocol — The Fossil</a></li> | |
| 305 | -<li><a href="private.wiki">Syncing, and Deleting Private Branches — Creating,</a></li> | |
| 306 | -<li><a href="custom_ticket.wiki">System — Customizing The Ticket</a></li> | |
| 307 | -<li><a href="tickets.wiki">System — The Fossil Ticket</a></li> | |
| 308 | -<li><a href="branching.wiki">Tagging — Branching, Forking, Merging, and</a></li> | |
| 309 | -<li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil — A</a></li> | |
| 310 | -<li><a href="../test/release-checklist.wiki">Testing Checklist — Pre-Release</a></li> | |
| 311 | -<li><a href="th1.md">TH1 Scripting Language — 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 — 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 — Customizing The</a></li> | |
| 328 | -<li><a href="tickets.wiki">Ticket System — The Fossil</a></li> | |
| 329 | -<li><a href="customgraph.md">Timeline Graph — Theming: Customizing the</a></li> | |
| 330 | -<li><a href="css-tricks.md">Tips and Tricks — Fossil CSS</a></li> | |
| 331 | -<li><a href="hints.wiki">Tips And Usage Hints — Fossil</a></li> | |
| 332 | -<li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> | |
| 333 | -<li><a href="gitusers.md">Translation Guide — Git to Fossil</a></li> | |
| 334 | -<li><a href="css-tricks.md">Tricks — 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 — 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 — Checkout vs</a></li> | |
| 339 | -<li><a href="hints.wiki">Usage Hints — 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 — Up and Running in 5 Minutes as a Single</a></li> | |
| 342 | -<li><a href="caps/">User Capabilities — 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 — Differences Between Setup and Admin</a></li> | |
| 345 | -<li><a href="serverext.wiki">Using CGI Scripts — 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 — Environment</a></li> | |
| 348 | -<li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> | |
| 349 | -<li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> | |
| 350 | -<li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> | |
| 351 | -<li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size — Image Format</a></li> | |
| 352 | -<li><a href="grep.md">vs POSIX grep — Fossil grep</a></li> | |
| 353 | -<li><a href="co-vs-up.md">vs Update — Checkout</a></li> | |
| 354 | -<li><a href="webui.wiki">Web Interface — The Fossil</a></li> | |
| 355 | -<li><a href="customskin.md">Web Pages — 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 — Lists of Commands and</a></li> | |
| 358 | -<li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — 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 — Check-Out</a></li> | |
| 363 | -<li><a href="aboutdownload.wiki">Works — How The Download Page</a></li> | |
| 364 | -<li><a href="aboutcgi.wiki">Works In Fossil — How CGI</a></li> | |
| 365 | -<li><a href="whyusefossil.wiki">You Should Use Fossil — 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> | |
| 366 | 133 | </ul></div> |
| 367 | 134 |
| --- 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 — Introduction to the</a></li> |
| 26 | <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> |
| 27 | <li><a href="fossil-from-msvc.wiki">2010 IDE — 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 — 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 — Contributor License</a></li> |
| 34 | <li><a href="alerts.md">Alerts And Notifications — Email</a></li> |
| 35 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 36 | <li><a href="blame.wiki">Algorithm Of Fossil — The Annotate/Blame</a></li> |
| 37 | <li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil — The</a></li> |
| 38 | <li><a href="customskin.md">Appearance of Web Pages — Theming: Customizing The</a></li> |
| 39 | <li><a href="hashes.md">Artifact Identification — Hashes: Fossil</a></li> |
| 40 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 41 | <li><a href="password.wiki">Authentication — 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 — The</a></li> |
| 44 | <li><a href="fossil_prompt.wiki">Bash Prompt — 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 — Differences</a></li> |
| 47 | <li><a href="hashpolicy.wiki">Between SHA1 and SHA3-256 — Hash Policy: Choosing</a></li> |
| 48 | <li><a href="blockchain.md">Blockchain? — Is Fossil A</a></li> |
| 49 | <li><a href="antibot.wiki">Bots — Defense against Spiders and</a></li> |
| 50 | <li><a href="private.wiki">Branches — 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 — The Fossil</a></li> |
| 54 | <li><a href="cap-theorem.md">CAP Theorem — Fossil and the</a></li> |
| 55 | <li><a href="caps/">Capabilities — Administering User</a></li> |
| 56 | <li><a href="caps/ref.html">Capability Reference — 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 — 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 — How</a></li> |
| 61 | <li><a href="changes.wiki">Changelog — Fossil</a></li> |
| 62 | <li><a href="chat.md">Chat — 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 — Check-in</a></li> |
| 67 | <li><a href="../test/release-checklist.wiki">Checklist — 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 — 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 — Hash Policy:</a></li> |
| 73 | <li><a href="chroot.md">Chroot Jail — Server</a></li> |
| 74 | <li><a href="contribute.wiki">Code or Documentation To The Fossil Project — Contributing</a></li> |
| 75 | <li><a href="style.wiki">Code Style Guidelines — Source</a></li> |
| 76 | <li><a href="../../../help">Commands and Webpages — 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 — Fossil Core</a></li> |
| 79 | <li><a href="cgi.wiki">Configuration Options — CGI Script</a></li> |
| 80 | <li><a href="server/">Configure A Fossil Server — How To</a></li> |
| 81 | <li><a href="rebaseharm.md">Considered Harmful — Rebase</a></li> |
| 82 | <li><a href="contact.md">Contact Information — Developer</a></li> |
| 83 | <li><a href="shunning.wiki">Content From Fossil — Shunning: Deleting</a></li> |
| 84 | <li><a href="defcsp.md">Content Security Policy — 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 — Benefits Of Version</a></li> |
| 88 | <li><a href="concepts.wiki">Core Concepts — Fossil</a></li> |
| 89 | <li><a href="newrepo.wiki">Create A New Fossil Repository — 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 — Questions And</a></li> |
| 92 | <li><a href="css-tricks.md">CSS Tips and Tricks — 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 — 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 — Theming:</a></li> |
| 97 | <li><a href="fossil-is-not-relational.md">Data Model — Introduction to the (Non-relational) Fossil</a></li> |
| 98 | <li><a href="tech_overview.wiki">Databases Used By Fossil — SQLite</a></li> |
| 99 | <li><a href="defcsp.md">Default Content Security Policy — 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 — Shunning:</a></li> |
| 102 | <li><a href="private.wiki">Deleting Private Branches — Creating, Syncing, and</a></li> |
| 103 | <li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm — Fossil</a></li> |
| 104 | <li><a href="delta_format.wiki">Delta Format — Fossil</a></li> |
| 105 | <li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> |
| 106 | <li><a href="theory1.wiki">Design Of The Fossil DVCS — 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 — Fossil</a></li> |
| 109 | <li><a href="pikchr.md">Diagram Language — 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 — Embedded Project</a></li> |
| 112 | <li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> |
| 113 | <li><a href="aboutdownload.wiki">Download Page Works — How The</a></li> |
| 114 | <li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> |
| 115 | <li><a href="quotes.wiki">DVCSes in General — 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 — Fossil Delta</a></li> |
| 119 | <li><a href="encryptedrepos.wiki">Encrypted Repositories — 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 — Webpage</a></li> |
| 123 | <li><a href="inout.wiki">Export To And From Git — Import And</a></li> |
| 124 | <li><a href="fossil-from-msvc.wiki">Express 2010 IDE — Integrating Fossil in the Microsoft</a></li> |
| 125 | <li><a href="serverext.wiki">Extensions — CGI Server</a></li> |
| 126 | <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts — Adding</a></li> |
| 127 | <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> |
| 128 | <li><a href="fileformat.wiki">File Format — 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 — The</a></li> |
| 131 | <li><a href="unvers.wiki">Files — Unversioned</a></li> |
| 132 | <li><a href="branching.wiki">Forking, Merging, and Tagging — Branching,</a></li> |
| 133 | <li><a href="delta_format.wiki">Format — Fossil Delta</a></li> |
| 134 | <li><a href="fileformat.wiki">Format — Fossil File</a></li> |
| 135 | <li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size — Image</a></li> |
| 136 | <li><a href="../../../md_rules">Formatting Rules — Markdown</a></li> |
| 137 | <li><a href="../../../wiki_rules">Formatting Rules — Wiki</a></li> |
| 138 | <li><a href="forum.wiki">Forums — 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 — 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 — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> |
| 160 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 161 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 162 | <li><a href="mirrorlimitations.md">Git Mirrors — 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 — Quotes: What People Are Saying About Fossil,</a></li> |
| 165 | <li><a href="mirrortogithub.md">GitHub — How To Mirror A Fossil Repository On</a></li> |
| 166 | <li><a href="globs.md">Glob Patterns — File Name</a></li> |
| 167 | <li><a href="env-opts.md">Global Options — Environment Variables and</a></li> |
| 168 | <li><a href="customgraph.md">Graph — Theming: Customizing the Timeline</a></li> |
| 169 | <li><a href="grep.md">grep — Fossil grep vs POSIX</a></li> |
| 170 | <li><a href="grep.md">grep vs POSIX grep — Fossil</a></li> |
| 171 | <li><a href="hacker-howto.wiki">Guide — Fossil Developers</a></li> |
| 172 | <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> |
| 173 | <li><a href="gitusers.md">Guide — Git to Fossil Translation</a></li> |
| 174 | <li><a href="style.wiki">Guidelines — 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 — 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 — Fossil Tips And Usage</a></li> |
| 181 | <li><a href="history.md">History Of Fossil — 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 — 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 — Hacker</a></li> |
| 191 | <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> |
| 192 | <li><a href="hashes.md">Identification — 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 — 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 — Developer Contact</a></li> |
| 197 | <li><a href="build.wiki">Installing Fossil — 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 — Fossil Repository</a></li> |
| 200 | <li><a href="webui.wiki">Interface — 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 — Server Chroot</a></li> |
| 205 | <li><a href="javascript.md">JavaScript in Fossil — Use of</a></li> |
| 206 | <li><a href="pikchr.md">Language — The Pikchr Diagram</a></li> |
| 207 | <li><a href="th1.md">Language — The TH1 Scripting</a></li> |
| 208 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 209 | <li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li> |
| 210 | <li><a href="interwiki.md">Links — Interwiki</a></li> |
| 211 | <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> |
| 212 | <li><a href="loadmgmt.md">Load — Managing Server</a></li> |
| 213 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 214 | <li><a href="loadmgmt.md"><b>Managing Server Load</b></a></li> |
| 215 | <li><a href="../../../sitemap">Map — 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 — The Backoffice</a></li> |
| 218 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 219 | <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> |
| 220 | <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> |
| 221 | <li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub — How To</a></li> |
| 222 | <li><a href="mirrorlimitations.md">Mirrors — Limitations On Git</a></li> |
| 223 | <li><a href="fossil-is-not-relational.md">Model — Introduction to the (Non-relational) Fossil Data</a></li> |
| 224 | <li><a href="globs.md">Name Glob Patterns — File</a></li> |
| 225 | <li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> |
| 226 | <li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> |
| 227 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 228 | <li><a href="alerts.md">Notifications — Email Alerts And</a></li> |
| 229 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 230 | <li><a href="pop.wiki">Operation — Principles Of</a></li> |
| 231 | <li><a href="cgi.wiki">Options — CGI Script Configuration</a></li> |
| 232 | <li><a href="env-opts.md">Options — Environment Variables and Global</a></li> |
| 233 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| 234 | <li><a href="index.wiki">Page — Home</a></li> |
| 235 | <li><a href="fileedit-page.md">Page — The fileedit</a></li> |
| 236 | <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> |
| 237 | <li><a href="customskin.md">Pages — 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 — File Name Glob</a></li> |
| 240 | <li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General — 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 — The</a></li> |
| 243 | <li><a href="defcsp.md">Policy — The Default Content Security</a></li> |
| 244 | <li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 — Hash</a></li> |
| 245 | <li><a href="grep.md">POSIX grep — 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 — Creating, Syncing, and Deleting</a></li> |
| 249 | <li><a href="makefile.wiki">Process — The Fossil Build</a></li> |
| 250 | <li><a href="contribute.wiki">Project — Contributing Code or Documentation To The Fossil</a></li> |
| 251 | <li><a href="embeddeddoc.wiki">Project Documentation — Embedded</a></li> |
| 252 | <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> |
| 253 | <li><a href="childprojects.wiki">Projects — Child</a></li> |
| 254 | <li><a href="fossil_prompt.wiki">Prompt — Fossilized Bash</a></li> |
| 255 | <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> |
| 256 | <li><a href="history.md">Purpose And History Of Fossil — The</a></li> |
| 257 | <li><a href="faq.wiki">Questions — 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 — 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 — User Capability</a></li> |
| 263 | <li><a href="backup.md">Remote Fossil Repository — Backing Up a</a></li> |
| 264 | <li><a href="image-format-vs-repo-size.md">Repo Size — Image Format vs Fossil</a></li> |
| 265 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 266 | <li><a href="encryptedrepos.wiki">Repositories — How To Use Encrypted</a></li> |
| 267 | <li><a href="backup.md">Repository — Backing Up a Remote Fossil</a></li> |
| 268 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 269 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 270 | <li><a href="mirrortogithub.md">Repository On GitHub — 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 — Markdown Formatting</a></li> |
| 273 | <li><a href="../../../wiki_rules">Rules — Wiki Formatting</a></li> |
| 274 | <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Up and</a></li> |
| 275 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 276 | <li><a href="cgi.wiki">Script Configuration Options — CGI</a></li> |
| 277 | <li><a href="th1.md">Scripting Language — The TH1</a></li> |
| 278 | <li><a href="serverext.wiki">Scripts — Adding Extensions To A Fossil Server Using CGI</a></li> |
| 279 | <li><a href="defcsp.md">Security Policy — The Default Content</a></li> |
| 280 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 281 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 282 | <li><a href="server/">Server — 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 — CGI</a></li> |
| 285 | <li><a href="loadmgmt.md">Server Load — Managing</a></li> |
| 286 | <li><a href="serverext.wiki">Server Using CGI Scripts — Adding Extensions To A Fossil</a></li> |
| 287 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 288 | <li><a href="caps/admin-v-setup.md">Setup and Admin Users — Differences Between</a></li> |
| 289 | <li><a href="hashpolicy.wiki">SHA1 and SHA3-256 — Hash Policy: Choosing Between</a></li> |
| 290 | <li><a href="hashpolicy.wiki">SHA3-256 — 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 — 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 — Image Format vs Fossil Repo</a></li> |
| 295 | <li><a href="customskin.md">Skins — 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 — 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 — Using</a></li> |
| 300 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 301 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| 302 | <li><a href="style.wiki">Style Guidelines — Source Code</a></li> |
| 303 | <li><a href="foss-cklist.wiki">Successful Open-Source Projects — Checklist For</a></li> |
| 304 | <li><a href="sync.wiki">Sync Protocol — The Fossil</a></li> |
| 305 | <li><a href="private.wiki">Syncing, and Deleting Private Branches — Creating,</a></li> |
| 306 | <li><a href="custom_ticket.wiki">System — Customizing The Ticket</a></li> |
| 307 | <li><a href="tickets.wiki">System — The Fossil Ticket</a></li> |
| 308 | <li><a href="branching.wiki">Tagging — Branching, Forking, Merging, and</a></li> |
| 309 | <li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil — A</a></li> |
| 310 | <li><a href="../test/release-checklist.wiki">Testing Checklist — Pre-Release</a></li> |
| 311 | <li><a href="th1.md">TH1 Scripting Language — 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 — 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 — Customizing The</a></li> |
| 328 | <li><a href="tickets.wiki">Ticket System — The Fossil</a></li> |
| 329 | <li><a href="customgraph.md">Timeline Graph — Theming: Customizing the</a></li> |
| 330 | <li><a href="css-tricks.md">Tips and Tricks — Fossil CSS</a></li> |
| 331 | <li><a href="hints.wiki">Tips And Usage Hints — Fossil</a></li> |
| 332 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 333 | <li><a href="gitusers.md">Translation Guide — Git to Fossil</a></li> |
| 334 | <li><a href="css-tricks.md">Tricks — 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 — 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 — Checkout vs</a></li> |
| 339 | <li><a href="hints.wiki">Usage Hints — 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 — Up and Running in 5 Minutes as a Single</a></li> |
| 342 | <li><a href="caps/">User Capabilities — 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 — Differences Between Setup and Admin</a></li> |
| 345 | <li><a href="serverext.wiki">Using CGI Scripts — 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 — Environment</a></li> |
| 348 | <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> |
| 349 | <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> |
| 350 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 351 | <li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size — Image Format</a></li> |
| 352 | <li><a href="grep.md">vs POSIX grep — Fossil grep</a></li> |
| 353 | <li><a href="co-vs-up.md">vs Update — Checkout</a></li> |
| 354 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 355 | <li><a href="customskin.md">Web Pages — 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 — Lists of Commands and</a></li> |
| 358 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — 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 — Check-Out</a></li> |
| 363 | <li><a href="aboutdownload.wiki">Works — How The Download Page</a></li> |
| 364 | <li><a href="aboutcgi.wiki">Works In Fossil — How CGI</a></li> |
| 365 | <li><a href="whyusefossil.wiki">You Should Use Fossil — 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 |
+39
-11
| --- 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> | |
| 2 | 2 | |
| 3 | 3 | <h2>No Server Required</h2> |
| 4 | 4 | |
| 5 | 5 | Fossil does not require a central server. |
| 6 | 6 | Data sharing and synchronization can be entirely peer-to-peer. |
| 7 | 7 | Fossil uses |
| 8 | 8 | [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types] |
| 9 | 9 | to ensure that (in the limit) all participating peers see the same content. |
| 10 | 10 | |
| 11 | -<h2>But, A Server Can Be Useful</h2> | |
| 11 | +<h2>But, a Server Can Be Useful</h2> | |
| 12 | 12 | |
| 13 | 13 | Fossil does not require a server, but a server can be very useful. |
| 14 | 14 | Here are a few reasons to set up a Fossil server for your project: |
| 15 | 15 | |
| 16 | 16 | 1. <b>A server works as a complete project website.</b><p> |
| @@ -27,22 +27,50 @@ | ||
| 27 | 27 | syncing their work.</b><p> |
| 28 | 28 | It is possible for developers to synchronize peer-to-peer but |
| 29 | 29 | that requires the developers coordinate the sync, which in turn |
| 30 | 30 | requires that the developers both want to sync at the same moment. |
| 31 | 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 | |
| 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 | |
| 35 | 36 | constantly to set up a peer-to-peer sync. |
| 36 | 37 | |
| 37 | 38 | 3. <b>A server provides project leaders with up-to-date status.</b><p> |
| 38 | 39 | 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. | |
| 42 | 43 | |
| 43 | 44 | 4. <b>A server provides automatic off-site backups.</b><p> |
| 44 | 45 | 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 | |
| 48 | 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. | |
| 49 | 77 |
| --- 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 @@ | ||
| 128 | 128 | * foreach VARIABLE-LIST VALUE-LIST BODY-SCRIPT |
| 129 | 129 | * if EXPR SCRIPT (elseif EXPR SCRIPT)* ?else SCRIPT? |
| 130 | 130 | * info commands |
| 131 | 131 | * info exists VARNAME |
| 132 | 132 | * info vars |
| 133 | + * lappend VARIABLE TERM ... | |
| 133 | 134 | * lindex LIST INDEX |
| 134 | 135 | * list ARG ... |
| 135 | 136 | * llength LIST |
| 136 | 137 | * lsearch LIST STRING |
| 137 | 138 | * proc NAME ARG-LIST BODY-SCRIPT |
| @@ -141,10 +142,11 @@ | ||
| 141 | 142 | * string compare STR1 STR2 |
| 142 | 143 | * string first NEEDLE HAYSTACK ?START-INDEX? |
| 143 | 144 | * string index STRING INDEX |
| 144 | 145 | * string is CLASS STRING |
| 145 | 146 | * string last NEEDLE HAYSTACK ?START-INDEX? |
| 147 | + * string match PATTERN STRING | |
| 146 | 148 | * string length STRING |
| 147 | 149 | * string range STRING FIRST LAST |
| 148 | 150 | * string repeat STRING COUNT |
| 149 | 151 | * unset VARNAME |
| 150 | 152 | * uplevel ?LEVEL? SCRIPT |
| @@ -168,10 +170,11 @@ | ||
| 168 | 170 | features of Fossil. The following is a summary of the extended commands: |
| 169 | 171 | |
| 170 | 172 | * [anoncap](#anoncap) |
| 171 | 173 | * [anycap](#anycap) |
| 172 | 174 | * [artifact](#artifact) |
| 175 | + * [builtin_request_js](#bireqjs) | |
| 173 | 176 | * [capexpr](#capexpr) |
| 174 | 177 | * [captureTh1](#captureTh1) |
| 175 | 178 | * [cgiHeaderLine](#cgiHeaderLine) |
| 176 | 179 | * [checkout](#checkout) |
| 177 | 180 | * [combobox](#combobox) |
| @@ -260,10 +263,21 @@ | ||
| 260 | 263 | |
| 261 | 264 | Attempts to locate the specified artifact and return its contents. An |
| 262 | 265 | error is generated if the repository is not open or the artifact cannot |
| 263 | 266 | be found. |
| 264 | 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 | + | |
| 265 | 279 | |
| 266 | 280 | |
| 267 | 281 | <a id="capexpr"></a>TH1 capexpr Command |
| 268 | 282 | ----------------------------------------------------- |
| 269 | 283 | |
| 270 | 284 |
| --- 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 |